domain-driven-design - 如何在 DDD 中正确定义聚合?

标签 domain-driven-design ddd-repositories

在 DDD 中设计聚合时的经验法则是什么?

根据 Martin Fowler 的说法,聚合是可以被视为单个单元的域对象的集群。聚合将其组件对象之一作为聚合根。

https://martinfowler.com/bliki/DDD_Aggregate.html

在设计了大约 20 个 DDD 项目之后,我仍然对选择创建聚合的域对象时的经验法则感到困惑。

Martin Fowler 使用订单和行项目进行类比,我认为这不是一个很好的例子,因为订单+行项目实际上是紧密绑定(bind)的对象。在这个例子中不需要考虑太多。

让我们尝试用汽车做类比,其中 CarContent 是汽车经销商域的子域。

CarContent 将包含至少一个或多个聚合。

例如,我们有这个Ag​​gregateRoot(我让它尽可能简单)

class CarStructureAggregate
{
     public int Id {get; private set;}
     public ModelType ModelType {get; private set;}
     public int Year {get; private set;}
     public List<EquipmentType> {get; private set;}
}

替代方案可能是这样的(示例 B)

class CarStructureAggregate
{
     public int Id {get; private set;}
     public ModelType ModelType {get; private set;}
     public int Year {get; private set;}
}

class CarEquipmentAggregate
{
    public int Id {get; private set;}
    public List<EquipmentType> {get; private set;}
}

汽车可以在没有设备的情况下创建,但在没有设备的情况下无法激活/发布(即可以通过两个不同的事务填充)

可以通过示例 A 中的 CarStructureAggregate 或示例 B 中的 CarEquipmentAggregate 引用设备。

EquipmentType 可以是一个枚举,也可以是一个具有更多类、属性的复杂类。

在示例 A 和 B 之间进行选择时的经验法则是什么? 现在想象一下汽车可能有更多信息,例如

  • 照片
  • 描述
  • 也许有更多有关引擎的数据

而 CarStructureAggregate 可能是一个非常大的类

那么是什么让我们将聚合拆分成新的聚合呢?尺寸?事务的原子性(尽管这不是问题,因为同一子域的聚合通常位于同一服务器上)

最佳答案

小心不要有过于强烈的面向对象思维。蓝皮书和 Martin Fowler 的帖子有点旧了,而且它提供的视野太狭窄。

聚合不一定是类。不需要坚持。这些是实现细节。甚至,有时,聚合所做的事情并不意味着改变,只是意味着“好的,可以完成这个操作”。

iTollu 帖子为您提供了一个良好的开端:重要的是事务边界。聚合的工作只是其中之一。确保操作中的不变量和域规则,在大多数情况下(记住并非总是如此),更改必须持久的数据。事务性边界意味着一旦总体表明某件事可以并且已经完成;世界上没有任何东西应该与它相矛盾,因为如果发生矛盾,那么你的聚合设计得很糟糕,与聚合相矛盾的规则应该是聚合的一部分。

因此,为了设计聚合,我通常从非常简单的开始并不断发展。想象一个静态函数,它接收检查操作的域规则所需的所有 VO、实体和命令数据(几乎是 DTO 所有数据,除了实体的唯一 ID),并返回一个域事件,表明某件事已经完成。事件的数据必须包含系统保存更改(如果需要)所需的所有数据,以及当事件到达其他聚合(在相同或不同的有界上下文中)时采取相应行动的数据。

现在开始重构和OO设计。抑制原始的痴迷反模式。添加约束以避免实体和 VO 的错误状态。用于检查或计算与实体相关的某些内容的代码更好地放入实体中。 Put your events in a diet 。将需要几乎相同的 VO 和实体来检查域规则的静态函数放在一起,创建一个类作为聚合根。使用存储库创建始终有效状态的聚合。还有很长一段时间等等。你知道;只是良好的 OOP 设计,走向无 DTO、“告诉,不要问”前提、责任分离等等。

当您完成所有工作后,您会发现您的聚合、VO 和实体是从域(有界上下文相关)和技术 View 完美设计的。

关于domain-driven-design - 如何在 DDD 中正确定义聚合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51243959/

相关文章:

domain-driven-design - 领域驱动设计与命令模式——相互排斥?

domain-driven-design - 跨限界上下文的实体建模

java - 聚合到 JPA 实体映射

dependency-injection - DDD : is it ok to inject a Repository into an Entity?

php - 如何在 Symfony2 项目中使用 Doctrine2 实现 DDD?

c# - 防止对象进入某种状态后被更改? (C#)

oop - 我的 DDD 逻辑属于哪里?

domain-driven-design - 我是否正确使用服务层?

domain-driven-design - 存储库模式 : how to Lazy Load? 或者,我应该拆分此聚合吗?

c# - 域驱动设计布局问题