DDD聚合:一致性边界 -James Hickey


在原始的域驱动设计书中,埃里克·埃文斯(Eric Evans)对聚合解决了哪些问题进行了评论:
需要保持适用于紧密相关的对象组的不变性,而不仅仅是离散的对象。
什么是不变式?
不变是业务规则,必须始终保持一致。
换句话说,我们的应用程序中经常会有跨越多个对象的业务规则。我们该如何处理?
聚合尝试解决两个主要问题:与业务规则和高争夺性有关的一致性(即数据库表中所谓的“锁”)。

示例:图书馆预订
如果图书馆有一个可供人们租用的庞大书籍/书卷怎么办?某个时刻只能一个读者租界这本大书,如果将这本书的章节拆分成多本书,减少争夺,则更多人可以分享这本书的内容。
假设所有这些作为对象存在于您的软件中,一个庞大的对象模型将是一致的,但是会遇到争用问题,很多人想租借同一本书籍怎么办?小对象将面临更少的争用,但是当多个用户试图修改这些小对象时,小对象之间将难以保持一致性。

事务一致性
考虑一下数据库事务。当我们将事务提交到数据库时,我们之所以这样做,是因为所有相关数据只是真正地连在一起。

不变性内的一致性
让我们采用一致性和不变性这两个概念,并将它们融合在一起。
假设您正在使用时间跟踪软件。
两位不同的经理正在增加为员工工作的时间。

这会很好,因为一位经理修改的日期与另一位经理修改的日期不同。实施的代码将每天分别获取,并且它们也存储为不同的数据库行。因此,这里没有争用问题。
当要求我们限制整个时间表的总工作量时会发生什么?

在此示例中,当前整个时间表状态目前是32小时,最大值不应超过40小时(这是我们的新业务规则)。两个经理各自在两个不同的日期(一个在周一,另一个在周二)增加了8个小时,两者都将从数据库中查询时间有32小时,这时第一个加入的8小时已经成功完成了,而另一个操作已经获取了时间状态还是32个工作小时(banq注:类似数据库ACID的脏读),那么第二个操作也会成功。这样就有了48小时的时间表(但是业务规则是最大值不应超过40小时)。这种情况表明,我们还没有创建某种一致性边界。

增加一致性边界
如果我们在整个时间表和相关对象周围添加边界,该怎么办?
我们可以将整个时间表视为所有可能发生的操作或其“内部”事物的“门户”。

现在,整个时间表上的任何操作都必须通过聚合体。这个聚合体好像一个保镖或代理者。​​​​​​

现在,我们已经在整个时间表上添加了一个一致性边界,我们可以控制对其执行哪些操作。我们的逻辑并未分散在系统的不同部分。
注意:是的,争用和锁定仍然存在问题。我们将在另一篇文章中介绍。

经验总结
将真实不变式建模为一致性边界。不变性应驱动我们的总体设计。
不变性根据业务规则,但是,一项业务可能没有复杂的规则,另一个可能有非常严格的规则。这将影响我们设计软件的方式,从而影响总体边界。

真正不变性
例如,假设我们正在建立一个视频游戏,两个人互相打架。
如果对手的健康点降低到0,会发生什么。但是,他们没有死?然而。
也许在几秒钟内,系统就会检查,然后对手就会死去。那有意义吗?没有。

真正的不变式是那些需要在与该规则相关的操作相同的交易中保持一致的变量。换句话说,业务规则执行以后没有必要被再次“检查”。“检查”或验证应该是在某些操作进行时。(banq注:高实时的事务性)
例如,如果在线保险申请中的一项规则是确保申请人的信用评分足够好以允许他们购买保险,该怎么办?
我们是否需要在申请人提交申请的同时进行检查?不。
我们可以稍后再做,该系统将很有意义。
我们可以说保险申请最终是一致的。在将来的某个时候,当我们“等待”系统执行操作时,我们知道事情最终会解决。
作为业务流程,有意义的是,提交的申请“正在”被批准(或拒绝)。
从事务的意义上讲,这些将不被视为真正的不变式。虽然,它们仍然是重要的业务规则。

结论
第一条经验法则:将真实不变式建模为一致性边界。花一些时间思考一下您的系统。什么是真正的不变量的呢?哪一个不是?如果根据相关不变量对对象建模,会发生什么?

原文点击标题
banq注:需要数据两个表以上使用外键等约束规则保持书籍一次性一致更改的,都可能需要放入一个聚合;或者,区分业务规则与业务流程,涉及到业务流程的长时间事务不能放入聚合,聚合跨时间边界太大,但是业务规则与业务流程有时很难区分,业务规则通过业务流程来实现的。参考:https://www.jdon.com/54022