DDD - 放宽聚合之间的最终一致性规则

DDD - Relaxing the rule of Eventual Consistency between aggregate

我正在阅读 Scott Millett 与 Nike Tune 合着的领域驱动设计模式、原则和实践一书。在第 19 章聚合中,他指出:

Sometimes it is actually good practice to modify multiple aggregates within a transaction. But it’s important to understand why the guidelines exist in the first place so that you can be aware of the consequences of ignoring them.

When the cost of eventual consistency is too high, it’s acceptable to consider modifying two objects in the same transaction. Exceptional circumstances will usually be when the business tells you that the customer experience will be too unsatisfactory.

To summarize, saving one aggregate per transaction is the default approach. But you should collaborate with the business, assess the technical complexity of each use case, and consciously ignore the guideline if there is a worthwhile advantage, such as a better user experience.

我在我的项目中遇到一个案例,当用户请求对我的应用程序进行操作并且该操作影响两个聚合时,并且有两个聚合必须验证的规则才能成功进行操作。 类似于 "Allocating a cell for a detainee":

  1. 用户提出请求
  2. 被拘留者 (AR1) 从数据库中获取并接收到命令:detainee.AllocateTo(cellId); 3 获取 Cell (AR2) 并接收命令:cell.Allocate(detaineeId);

第 2 步和第 3 步都可能引发异常,具体取决于被拘留者的身份和牢房容量。但是抽象它。

使用最终一致性,如果步骤 2 成功执行,发出事件 DetaineeAllocated,但步骤 3 失败(将 运行 在另一个事务中,在事件处理程序内),聚合的状态将不一致,更糟糕的是,该操作似乎已为用户成功执行。

我知道有像"when the user makes a purchase over $ 100, its type must be changed to VIP"这样的情况可以使用最终一致性来实现,但是我说的这种情况好像没有。

你认为这是书上提到的特例吗?

"the state of aggregates will be inconsistent"

嗯,它不应该永远不一致,否则就不会最终一致。您通常会与业务专家讨论以确定可接受的一致性时间范围。

如果出现问题,将引发一个事件,该事件应触发补偿操作,并且可能会向人类发出通知,说明最终出现了问题。

另一种方法是引入流程管理器,负责通过触发命令和监听事件来执行业务流程,直到完成或超时。 AR 通常设计为允许小的增量步骤 走向一致性。例如,可以有一个命令先保留牢房 space,而不是直接分配被拘留者。如有必要,UI 始终可以轮询进程状态以了解其何时完成。

最终一致性显然是有代价的。如果您在不需要极端可扩展性的单体中有一个数据库,您很可能希望在单个事务中修改两个 AR,直到这成为一个问题。 最终一致性通常被认为比强一致性成本更低,但我相信这主要适用于您必须处理 XA 事务的分布式系统。

每个聚合不得有无效状态(内部状态),但这并不意味着聚合必须彼此一致(外部或系统状态)。

鉴于您问题的背景,答案可能是肯定的,也可能不是。

外部状态可以变得最终一致,这可能是您的产品负责人可以接受的。在这种情况下,您设计方法来检测不一致并处理它(例如,通过重试操作、发布补偿事务等)

是的理由

在您的编排层中,继续更新事务中的聚合。您可能选择这样做是因为它“简单”且“正确”,或者您可能选择这样做是因为您的产品负责人表示无论出于何种原因都不能容忍这种不一致。

否的另一种情况

还有另一种说法,这不是特例,不是多次交易的原因。这条出路需要改变你的模型。考虑消除被拘留者与牢房之间的相互依赖关系,而是引入另一个聚合 CellAssignment,它表示可以在单个事务中构建和保存的 moment-interval(时间关系)。在这种情况下,您的被拘留者和牢房不会改变。

Do you think that this is a special case that the book mentions?

没有

我怀疑你这里有一个建模错误。

根据你的描述,听起来你正在处理类似 CellAssignment 的事情,你试图维护的不变量是确保活动单元格分配之间没有冲突。

这向我暗示您缺少某种汇总 - 类似于座位表? - 跟踪所有活动分配和冲突。

你怎么知道?一种方法是绘制聚合图形;为您需要保存的每条信息创建一个节点,如果存在需要锁定两个节点的规则,则用线连接节点。如果您发现自己的图表断开连接,或者两个图表仅在根 ID 处连接,那么将一些信息分离到一个新图表中将改进您的建模是一个不错的选择。

All Our Aggregates Are Wrong,作者 Mauro Servienti,将是一篇很好的评论。