12

我正在与聚合和聚合根作斗争。我有一个自然聚合根,它适用于大约 60% 的用户请求。即这些请求自然适用于聚合根。

在我的聚合中,我有另一个实体,它只能作为聚合根的成员存在。然而,用户将被告知这个其他实体对象。从概念上讲,有时用户直接对这个非聚合根对象进行操作是有意义的。

所以,我想我有几个选择:

  1. 根据用户请求的操作,它们都可以是聚合根。
  2. 所有操作都必须经过顶级聚合根。

请注意,顶级聚合根将保存此其他实体的集合。


例子:

主骨料根:车

第二个实体:座位(根据类型,汽车有 2 个或 4 个座位)。在我的域中,座位只能作为汽车的一部分存在。

域中的大多数操作都在 Car 级别。所以这将是聚合根的一个很好的候选者。然而,(我在这里举个例子很困难),一些操作将在座位级别,例如 SpillCoffee、ChangeFabric、Clean...。

Seat 和 Car 都可以是聚合根吗?还是我应该总是从 Car 开始?

谢谢

4

3 回答 3

13

聚合的想法是保证一致性,是负责数据完整性和强制不变量的根。

假设有一条规则,比如“所有座位的面料必须相同”,或者“只有当车内有人时,你才能将咖啡洒在座位上”。一旦客户愿意,执行这些规则将变得更加困难。能够单独更改结构,否则这些不变量将需要强制在外部(危险区域)。

恕我直言,如果完整性或强制不变量不是问题,则不需要聚合。但是,如果有必要,我的建议是从汽车开始一切。但总是想到模型。如果有这样的不变量,那么谁来执行这些不变量?然后尝试将这个想法传递给代码,一切都应该没问题。

于 2009-06-10T17:02:55.887 回答
2

可能您需要对领域模型的某些方面有更深入的了解。这个问题表明您即将发明一种方法来组织实体以提供系统,理想情况下,此类问题在实施之前已经得到回答。

当这仅在系统实现上弹出时,您是否返回查看域,或者您发现一些脆弱性,其反馈可以 - 并且应该 - 汇总业务相关细节的更改,以使域更丰富和更好地建模。

在汽车示例中,我使用了关联不同上下文的两个聚合的方法。第一个是“汽车有座位”的方法,在这个聚合中,“座位”的可能动作将只是“作为汽车的一部分的座位”有意义的动作。例:清洁。

第二个聚合将在“seat”的上下文中,并且作为独立的seat 会有可能的操作和配置。示例:ChangeFabric、ColorList。这样,聚合“汽车”有“座位”,但客户可以在有意义的上下文中知道座位。这很危险,就像 samuelcarrijo 在上一篇文章中所说的那样。如果上下文之间的修改影响了域的完整性,那么您就失去了所有聚合概念。

于 2009-06-12T18:24:06.960 回答
1

对于带有购物车和订单项的购物车,我将这两者都作为聚合根,因为我经常独立修改它们。

public class Cart : IAggregateRoot
{
  public List<LineItem> LineItems {get;}
}

public class LineItems : IAggregateRoot
{
  public List<LineItem> LineItems {get;}
}

但是,我有一个单独的订单有界上下文,在这种情况下,我只需要一个聚合根,因为我不再需要单独修改行项目。

public class Order : IAggregateRoot
{
  public List<LineItem> LineItems {get;}
}

另一种选择是有一种从子 ID 查找聚合根的方法。

Car GetCarFromSeatID(guid seatID)
于 2009-06-10T17:06:48.453 回答