管理聚合边界 (DDD)
Manage aggregate boundaries (DDD)
我无法理解如何在 DDD 中分离聚合。至少,我有一个不知道如何解决的利益冲突。据我了解,在 DDD 中,聚合被定义为执行不变量/一致性的事务边界。然而,这导致我的聚合体庞大且难以维护。
我的用例是处理电子商务订单的系统。根据业务需求的基本流程如下:
- 外部系统(购物车)创建一个
Order
,包含送货地址、账单地址和订单项。
- 通过创建一个
FulfillmentOrder
来完成订单,该 FulfillmentOrder
被发送到外部履行服务提供商。
- 外部履行服务发送货物并为给定的履行订单创建一个或多个
Shipment
。每批货件包括已发货的行项目、出发国家(此外部服务的仓库所在国家/地区)和目的地国家/地区(===送货地址国家/地区)
- 为每个
shipment
创建一个 Invoice
,并根据 Shipment
的出发地和目的地国家/地区计算增值税/销售税。
这是域的简化版本。如您所见,有一系列明确定义的步骤,但存在一些纠缠......步骤取决于来自先前步骤和不同实体的数据。最重要的是:Order
和 Shipment
之间的关系(因此 Order
和 Invoice
)是 1:many 而不是 1:1.
基于上面的聚合定义,我会将描述的用例设计为一个限界上下文(订单处理),其中 Order
是我的聚合根,Fulfillment Order
、Shipment
和Invoice
s 是由聚合根管理的实体集合。
有几个不变量,跨越所有多个实体,例如:
- 所有发票的总金额不得超过订单总额。
- 您不能创建包含非现有订单项目的发票。
- 装运项目数量不得超过触发装运的履行订单的数量。
将顺序作为唯一的聚合根可以强制执行这些不变量。但是这样会导致订单聚合体庞大,难以维护。
另一方面,我可以将订单、履行订单、装运和发票分别建模为聚合。这将使每个聚合更苗条和更集中。但是我失去了强制执行上述不变量的能力(这显然对业务不利,但也违反了 DDD 原则)
在领域建模方面是否有任何指导,或者对策略的优点/缺点进行了一些比较?
Is there any guidance in domain modelling in which direction to go or some comparisons of benefits / drawbacks of strategies?
一个好的起点是 Memories, Guesses, and Apologies (Helland - 2007)。
聚合边界是由领域动态驱动的:哪些信息必须一起改变。 “必须一起改变”有一个时间方面——我们不是在谈论当系统达到 平衡 时必须保持一致的信息,而是在事情发生变化时必须保持哪些数据关系。
要区分这两者,您必须非常注意以下问题:如果此报告中的信息已过时,企业将付出什么代价?
You cannot create an invoice that includes a non exiting order item.
为什么不呢?发生这种情况时,企业的成本是多少?
A microsecond difference in timing shouldn’t make a difference to core business behaviors. -- Udi Dahan, 2010
您在建模时必须小心,不要假设信息会立即通过您的系统传输。特别是在接触现实世界的系统中,信息以有限的速度传播,业务流程包括补偿协议。
我无法理解如何在 DDD 中分离聚合。至少,我有一个不知道如何解决的利益冲突。据我了解,在 DDD 中,聚合被定义为执行不变量/一致性的事务边界。然而,这导致我的聚合体庞大且难以维护。
我的用例是处理电子商务订单的系统。根据业务需求的基本流程如下:
- 外部系统(购物车)创建一个
Order
,包含送货地址、账单地址和订单项。 - 通过创建一个
FulfillmentOrder
来完成订单,该FulfillmentOrder
被发送到外部履行服务提供商。 - 外部履行服务发送货物并为给定的履行订单创建一个或多个
Shipment
。每批货件包括已发货的行项目、出发国家(此外部服务的仓库所在国家/地区)和目的地国家/地区(===送货地址国家/地区) - 为每个
shipment
创建一个Invoice
,并根据Shipment
的出发地和目的地国家/地区计算增值税/销售税。
这是域的简化版本。如您所见,有一系列明确定义的步骤,但存在一些纠缠......步骤取决于来自先前步骤和不同实体的数据。最重要的是:Order
和 Shipment
之间的关系(因此 Order
和 Invoice
)是 1:many 而不是 1:1.
基于上面的聚合定义,我会将描述的用例设计为一个限界上下文(订单处理),其中 Order
是我的聚合根,Fulfillment Order
、Shipment
和Invoice
s 是由聚合根管理的实体集合。
有几个不变量,跨越所有多个实体,例如:
- 所有发票的总金额不得超过订单总额。
- 您不能创建包含非现有订单项目的发票。
- 装运项目数量不得超过触发装运的履行订单的数量。
将顺序作为唯一的聚合根可以强制执行这些不变量。但是这样会导致订单聚合体庞大,难以维护。
另一方面,我可以将订单、履行订单、装运和发票分别建模为聚合。这将使每个聚合更苗条和更集中。但是我失去了强制执行上述不变量的能力(这显然对业务不利,但也违反了 DDD 原则)
在领域建模方面是否有任何指导,或者对策略的优点/缺点进行了一些比较?
Is there any guidance in domain modelling in which direction to go or some comparisons of benefits / drawbacks of strategies?
一个好的起点是 Memories, Guesses, and Apologies (Helland - 2007)。
聚合边界是由领域动态驱动的:哪些信息必须一起改变。 “必须一起改变”有一个时间方面——我们不是在谈论当系统达到 平衡 时必须保持一致的信息,而是在事情发生变化时必须保持哪些数据关系。
要区分这两者,您必须非常注意以下问题:如果此报告中的信息已过时,企业将付出什么代价?
You cannot create an invoice that includes a non exiting order item.
为什么不呢?发生这种情况时,企业的成本是多少?
A microsecond difference in timing shouldn’t make a difference to core business behaviors. -- Udi Dahan, 2010
您在建模时必须小心,不要假设信息会立即通过您的系统传输。特别是在接触现实世界的系统中,信息以有限的速度传播,业务流程包括补偿协议。