【问题标题】:Should we complicate aggreggate root due to an edge case?由于边缘情况,我们是否应该使聚合根复杂化?
【发布时间】:2020-01-09 07:57:44
【问题描述】:

假设我们正在使用事件溯源实现支付系统,并且有诸如 PaymentCreated、PaymentAuthorized、PaymentSettled 和 PaymentInvoiced 之类的事件

很明显,Payment 应该是我们的聚合根,所有操作都在它上面进行。

但也有退款。退款基本上是一种付款,唯一的区别是它的金额为负数,并且它必须引用我们正在退款的付款。在所有其他方面,它的行为类似于正常付款。它具有相同的状态和事件集,所有外部系统都以与正常付款相同的方式处理退款。

为了更有趣,可以有多个部分退款,但退款金额总和必须小于或等于原始付款金额。

我们基本上有两种选择如何实现它。

a) 作为总根的付款 - 缺点是在检查退款约束时,我们必须锁定我们正在退款的付款并检查其他退款。换句话说,退款初始化会使用多个聚合根进行操作,这是不推荐的。

b) “支付组”作为聚合根 - 我们可以将原始付款及其所有退款放入一个聚合中,该聚合可以强制执行不变量。缺点是系统中的大多数操作都在一个支付/退款级别上执行,因此模型与用例不匹配。假设第 3 方通知我们付款/退款已结算。我们得到一个外部 ID,我们必须通过这个 ID 找到支付组并在那里找到支付(使用相同的 ID)。这似乎不必要地使大部分代码复杂化,只是为了确保退款中的一个不变性。

你会推荐什么?

【问题讨论】:

  • 我没有得到如果在选项“a”中你的意思是将Payment 作为聚合根,并带有Refund 实体的集合。这对我来说似乎是“最简单”的解决方案,但也许我错过了一些东西。
  • 选项 a) 表示将付款和退款保持为独立的独立集合
  • 考虑到你的不变量,我会将它们放在同一个聚合中,这样你就可以保证数据的一致性,因为所有数据都在同一个“锁”上,你可以确保数据不会结束起来,有什么不好的?

标签: architecture domain-driven-design event-sourcing aggregateroot


【解决方案1】:
  1. 您的业务限制是退款金额不能低于原始付款金额。
  2. 可能会部分退款,因此每笔交易都必须经过此验证。
  3. 途中可能会有多笔退款,因此您需要确保退款检查和处理是作为一笔交易的一部分进行的。

因此,您的退款和付款属于同一事务一致性边界的一部分。应该有一个聚合,Payment,并且您的 Refunds 必须是其中的一部分。所有退款交易都必须通过Payment 聚合路由。

在 DDD 术语中,Refund 将是您的域模型中的一个实体,属于 Payment聚合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-15
    • 2013-05-16
    • 1970-01-01
    • 1970-01-01
    • 2013-10-25
    • 2015-08-11
    • 1970-01-01
    相关资源
    最近更新 更多