database - 对基于集合的 sql 数据操作操作的代码列表进行建模

标签 database domain-driven-design metaprogramming dsl

像 LINQ 这样的技术在描述关系数据查询方面做得很好,类型如 IQueryable , IGrouping , 和 IOrderedQueryable建模投影、选择、聚合、排序等。这些来自关系代数的概念允许我们在一台机器上用一种语言交流一个相当任意的查询,并在不同的机器上用不同的语言 (~sql) 执行它。

如果能够对更复杂的多部分查询甚至涉及 INSERT 的数据操作命令执行相同的操作,那就太好了。 s, UPDATE s 和 DELETE s 可以描述完整的操作,而无需首先在应用层检索/水合数据的开销,这是对象关系映射器或 ORM 的典型特征。

在应用程序中,我们可以描述一个像 Delete all 这样的操作。客户 (以及他们的 订单 ),其最近的 订购 超过 2 年(此外,假设没有为该关系启用级联删除)。这当然比带有 t-sql 脚本的 ADO 更有效,但不能在 ORM 中完成,而没有在应用程序层中选择、传输、混合和跟踪数据以及可能发出单独的删除命令的开销。 (也许有一些可用于 ORM 的优化可以在某些情况下更有效地执行此操作,但通常 AFAIK 他们不能)当然,发布 t-sql 脚本的问题是语句中没有类型检查,也没有任何参数或返回数据。

能够为远程执行建模这些任意命令的一个惊天动地的优势是,除了减少运行时处理和网络聊天开销之外,还可以在应用层中对域范围的不变量进行编码和注册,然后可以将这些不变量与任何临时命令。

我们可能有一个愚蠢的域不变式 A对于所有 客户 , 总和 客户 订单' 价格不能超过 $10,000,000.00,除非 位是 1 和另一个愚蠢的域不变量 B说对于所有 客户 , 姓氏 不能包含三个以上的下划线(尽管这些下划线可能可以通过数据库引擎本身的检查约束或触发器的 native 机制强制执行)。然后当我们发出更新现有的命令时 订单 价格 ,系统通过静态分析可以知道不变量A可能会因为命令和不变性而被违反 B不能,因此系统会发出一些断言 A在原始命令之后。整个发出的脚本将被包装在一个事务中(如果断言失败则回滚)并且可以自动缩小不变量以仅针对特定的 断言规则。客户的一套订单而不是不必要地重新检查所有 客户的总计。我相信这种优化的、集中的、DRY 的业务规则编码/执行在今天的产品中是不可能的。

为了实现这种潜力,我认为我们需要一个代数(超出 SELECT 的关系代数)来描述 INSERT、UPDATE 和 DELETE(统称为 DML)的任意数据操作,甚至像中间计算这样的东西值,如用于计算的临时表,在 t-sql 中表示为多语句列表。

不幸的是,我一直无法找到关于将 DML 形式化为代数的研究,或者能够对其进行建模,或者此类元编程。尽管 Tutorial-D 和 jOOq 似乎为讨论提供了一些东西 - 我只是不知道如何提取它。你能解释清楚吗?

一些我认为很有值(value)的讨论,但我想避免用它来填写评论:

Are you suggesting that domain models aren't a good fit to protect invariants and establishing transactional boundaries? The invariants you mentioned aren't hard to protect using a proper domain model. What problem are you trying to avoid exactly?



– 普拉克斯

As I understand it, large domains in typical ddd require bounded contexts to avoid having to hydrate large subsets of the data into the application layer for validation. I am trying to avoid that overhead. Also, domain invariants must be non-trivially restated for each bounded context, which is error-prone. By modeling the operations for remote execution, we get smarter/smaller/faster/more correct code.

In some core library, the domain could be modeled and the invariants registered. Then consumers of that library, such as for a web service, could then construct type-checked descriptions of arbitrary operations without explicit consideration for bounded contexts or particular invariants. The domain core offers to its consumers "this is the full range of what you can do over this domain" and (perhaps) the service code offers to its clients "these are the exact features we're offering".



– uosɐſ

I'm not sure if you understood correctly what a Bounded Context is and how they might communicate with each-other. "Also, domain invariants must be non-trivially restated/maintained for each bounded context which is error-prone" There's usually just one context that have data ownership and that context shall be responsible for invariants involving it's own data. For instance, imagine a company that sells goods on Internet. They might have an Inventory context where products gets maintained and a Shopping context that listen to newly available products from the Inventory.



– 普拉克斯

I'm not very much arguing against current ddd techniques, so I'm not choosing excellent examples against them. I'm more interested in this alternative arrangement which I intuit would be more natural and advanced than current ddd techniques. I've seen data models that are extremely intertwined and don't offer obvious boundaries (perhaps poorly designed, OK). I expect that this way could be boundaryless AND more performant.



– uosɐſ

If there was a rule that a Product name couldn't contain the word "propaganda" it would be enforced only in the Inventory context. If we were to duplicate invariants of every contexts in every other contexts it would indeed become a maintenance nightmare.



– 普拉克斯

But you plausibly might have a bounded context centered on Customers and a second bounded context centered on Orders. And maybe the $10,000,000.00 Limit I mentioned is made to be a column in Customer (and therefore variable), so this business rule can be violated in two ways: either by dropping that Limit on Customer or increasing totals in Order. So non-trivially reciprocal rules must check for violations in either bounded context depending on the change. Our system could decide to skip the assertion if Prices and Limits aren't changed, which would be pretty slick, no? In the traditional ddd, you might also need some optimized variants for bulk manipulations (Add an Order of $1000 to every Customer) which could be automatically derived by our new system.



– uosɐſ

最佳答案

看起来不太可能,你不需要的一件事是“超越”关系代数的东西。这根本不是一个理论问题,而是一个想象力和工程问题。您所谈论的问题跨越多个领域:编程语言、库支持和 DBMS。它可以(并且应该)完成。但首先它需要被普遍理解为现实和可取的,而我们还没有做到。

就代数而言,缺少的只是赋值。如果你读过 Date 的第三个宣言,你可能还记得插入/更新/删除只是赋值的变体:

S += f(R)        -- insert
S += f(R) - g(S) -- update
S -= f(R)        -- delete

(Python 用它的标准库中的 set 类很好地证明了这一点,顺便说一句,除了您没有开箱即用的元组集合运算符。)

所以这不是一个理论问题;代数很好。而且您也不是纯粹询问语法。在我看来,您想要的是一个可以在功能上操作的 DBMS,而无需 SQL —— 和 SQL 生成器 —— 作为中介。如果您的数据库中的表在您的编程语言中显示为变量,并且有一个支持选择、项目和连接的关系代数库(对于该语言),那不是很好吗?

就此而言,为什么不将关系运算符合并到正确的语言中呢?为什么在关系理论发明 40 年后,它的使用仅限于数据库?事实上,数十年来,这一直是数据库社区的遗憾。虽然已经完成 - 参见。例如,Datalog——我们近年来看到的大量新语言以延续不支持集合论运算的 C 传统而著称。

然而,碰巧的是,仅仅将关系和关系运算符内置到语言中是不够的。编程语言通常希望定义它们的变量,并排他地拥有它们。这实际上是编程语言的定义:定义和操作内存卡的东西,其生命周期受程序执行的限制。有趣的数据通常从“那里”开始,在某个地方,而不是在程序内存中。

所以,你真正、真正想要的是操作“数据库中”的数据,就好像这些表是程序变量(也称为远距离操作),然后一些 super 方便、理想情况下透明的方法将结果移入程序存储器。就像,哦,任务。要在这个方向上取得任何进展,您需要 DBMS 的合作。

如今,为了与典型的 DBMS 进行交互,您需要用其语言(通常是 SQL)来表述您的问题,并将输出一行一行地提取到程序内存中。这是一个 I/O 模型:写入字符串,读取结果。要将 I/O 从编程模型中取出,您需要一个不同的 API,更像是 RPC。如果编程语言和 DBMS 使用相同的数据模型(关系)和函数(关系代数)和数据类型,那么您就有机会以相同的方式操作远程和本地数据。

那是套房:
  • 关系和关系操作的语言支持
  • 本地和机器外变量的语言识别
  • DBMS 支持以编程方式公开表定义,以便编译器/解释器可以“链接”到它们,作为库符号
  • DBMS 支持远程调用关系运算符,逐个函数,而不是逐个语句

  • 您可能已经注意到,就合理的近似而言,没有人试图执行上述操作。语言设计者普遍忽略集合论和谓词逻辑。 DBMS 供应商——以及流行的免费项目——受制于 SQL,对修复 SQL 的集合论缺陷或通过逻辑函数 API 暴露他们的系统完全不感兴趣。任何人的脑海中最远的事情就是开发一组一致的类型和运算符。

    那么我们有什么? Linc 是跳舞熊的一个很好的例子,它将字符串和原始类型的 SQL 拼凑在一起,通过管道将其喷出,并将数据库表表示为宿主语言提供的逐行操作。考虑到当今环境的现实,这是一个相当不错的节目。但是,正如你的问题所暗示的那样,新鲜感会消失,工作也不会变得更容易。不过,您可能想保留您的机票:根据目前的速度和方向判断进展情况,您将在同一个座位上再坐 40 年。

    关于database - 对基于集合的 sql 数据操作操作的代码列表进行建模,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32894724/

    相关文章:

    mysql - 无法在 MySQL 中为相关架构建表

    database - ER图中2个实体之间的2个关系

    domain-driven-design - DDD : inheritance and transactions

    c# - DDD - 实体状态转换

    C++ 可变参数模板类型过滤转换

    database - Couchdb 架构 : Views or documents?

    mysql - 合并各个表?

    c# - 我应该将存储库接口(interface)与域模型分离吗

    用于将正则表达式解析为 AST 的 Java 库?

    ruby - 有没有办法动态地将范围添加到事件记录类?