【问题标题】:DDD - Relaxing the rule of Eventual Consistency between aggregateDDD - 放宽聚合之间的最终一致性规则
【发布时间】:2020-09-07 09:53:57
【问题描述】:

我正在阅读由 Scott Millett 使用 Nike Tune 撰写的《域驱动设计的模式、原则和实践》一书。在第 19 章“聚合”中,他指出:

有时在一个事务中修改多个聚合实际上是一种很好的做法。但它是 重要的是要首先了解这些指南存在的原因,以便您了解 忽视它们的后果。

当最终一致性的成本太高时,考虑在同一个事务中修改两个对象是可以接受的。例外情况通常是企业告诉您客户体验太不令人满意。

总而言之,每个事务保存一个聚合是默认方法。但是你应该 与业务协作,评估每个用例的技术复杂性,并有意识地忽略 如果存在有价值的优势(例如更好的用户体验),则为指南。

我在我的项目中遇到一个案例,当用户请求对我的应用程序进行操作并且该操作影响两个聚合,并且必须通过两个聚合验证操作才能成功执行规则。 类似于“为被拘留者分配牢房”:

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

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

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

我知道有类似“当用户购买超过$ 100,其类型必须更改为VIP”的情况可以使用最终一致性来实现,但我提到的情况似乎不是一个。

你认为这是书中提到的一个特例吗?

【问题讨论】:

    标签: domain-driven-design eventual-consistency


    【解决方案1】:

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

    根据您问题的背景,答案可能是肯定的,也可能是否定的。

    拒绝的理由

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

    是的情况

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

    否的另一种情况

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

    【讨论】:

    • 我的 PO 说如果命令之间发生故障,用户体验似乎很差。所以,在这种情况下,我会选择是。谢谢。
    • 除此之外,创建CellAssignment,会影响Cell聚合,应该减少它的可用容量,创建CellAssignment前需要检查
    【解决方案2】:

    “聚合的状态会不一致”

    嗯,它不应该永远不一致,或者那不会是最终的一致性。您通常会与业务专家讨论以确定可接受的一致性时间框架。

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

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

    最终的一致性显然是有代价的。如果您在一个不需要极高可扩展性的单体架构中拥有一个单独的数据库,那么您很可能倾向于在单个事务中修改两个 AR,直到这成为问题。 最终一致性通常以比强一致性更便宜的价格出售,但我相信这主要用于必须处理 XA 事务的分布式系统。

    【讨论】:

      【解决方案3】:

      你认为这是书中提到的一个特例吗?

      没有。

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

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

      这向我表明您缺少某种汇总信息——比如座位表? - 跟踪所有活动的分配和冲突。

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

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

      【讨论】:

      • 对于整个单元格集的单个 AR 会不会比仅修改 2 个 AR 更增加争用?
      • "视情况而定";如果您发现 CellAssignment 确实是一个单独的聚合(由其自己的表支持!),那么您可以独立于其他所有内容调整数据存储的这一部分,并选择最适合您的流量的锁设计。
      • 之前的评论应该被理解为猜想;在这个范围内,我没有足够的个人圈数来提出强有力的未经证实的主张。
      猜你喜欢
      • 1970-01-01
      • 2020-10-24
      • 1970-01-01
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多