在聚合之外聚合根和值对象

时间:2014-03-23 11:35:17

标签: domain-driven-design aggregate aggregateroot value-objects

我有一个聚合根“Car” 汽车有一个包含“Wheel”对象的值对象“Wheels”列表。 由于汽车不应该没有车轮(至少根据我们的业务逻辑),为了构建汽车,这在适当的域驱动设计中是有效的:

double radius = 17.0;
List<Wheel> carWheels = new List<Wheel>();
carWheels.add(new Wheel(radius));
Car aCar = new Car(carWheels);

我的问题基本上是,为了构造聚合根(在构造函数中传递值对象),在聚合根之外实例化值对象是一种好习惯。 我不想在无效状态下创建聚合根,并且希望遵循最佳实践。如果上面的代码不是最佳实践,应该怎么做?

3 个答案:

答案 0 :(得分:2)

恕我直言,这既不是一种不好的做法,也不是一种好的做法。全部取决于您尝试建模的实际域。在某些情况下,在聚合之外创建这些VO可能是有意义的,而在其他情况下,它只会打开您的域以进行恶意使用。 DDD迫使您忘记一些技术问题和不良/良好实践,以便专注于实际领域:

  1. 在任何情况下创造一辆26轮的汽车会有意义吗?您的示例模型允许
  2. 在任何情况下创造一个有4个车轮的车都有意义,每个车轮都有不同的半径?您的示例模型允许
  3. 创建半径为17.3284546的车轮是否有意义?同样,您的模型允许这种情况发生
  4. 因此,在我看来对于您已经展示的示例,最好在聚合内部处理这些不变量,因为您可以很好地约束事物的数量而不是在创建两者时对汽车和车轮完成。然而,这来自对域本身的仔细研究,而不是依赖于广为人知的好的或坏的做法。重申一下,通过在聚合之外创建VO,您将会有更好的状态。一切都取决于域名。

答案 1 :(得分:1)

我喜欢这种方法。在测试用例中,我们可以将存根/模拟轮注入汽车。

如果在车轮或汽车的构造中存在复杂的业务限制,我会介绍一个CarFactory。

答案 2 :(得分:1)

在这个具体的例子中,我将半径赋予Car的构造函数并让它自己构建轮子。这样,您可以隐藏客户代码中的实例化详细信息(您的汇总之外的知识较少),并且您的客户不会受到Car聚合内部更改的影响。

您应该只在一步/操作/操作中构建聚合。如果它不只是一步,那么您的汇总不可避免地会显示其内部结构的一些细节。