领域驱动设计 - 聚合根

时间:2009-06-10 16:03:22

标签: domain-driven-design aggregates

我正在努力研究聚合和聚合根。我有一个自然聚合根,适用于大约60%的用户请求。即这些请求自然适用于聚合根。

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

所以,我认为我有几个选择:

  1. 根据用户请求的操作,它们都可以是聚合根。
  2. 所有操作都必须通过顶级聚合根。
  3. 请注意,顶级聚合根将包含此其他实体的集合。


    示例:

    主要聚合根:汽车

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

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

    座椅和汽车都可以是聚合根吗?或者我应该始终从汽车开始?

    由于

3 个答案:

答案 0 :(得分:13)

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

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

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

答案 1 :(得分:2)

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

当仅在系统实施中弹出时,您是回去查看域还是发现了一些脆弱性,其反馈可以 - 而且应该 - 聚合对业务相关细节的更改,以使域更丰富,更好地建模。

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

第二个聚合将在“座位”的背景下,并且座位可能作为独立的可能行动和配置。示例:ChangeFabric,ColorList。通过这种方式,聚合“汽车”具有“座位”,但客户可以知道有意义的上下文。哪个是危险的,就像samuelcarrijo在上一篇文章中所说的那样。如果上下文之间的修改影响域完整性,则会丢失所有聚合概念。

答案 2 :(得分: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)