我有一个聚合根"Car"汽车有一个值对象"Wheels"列表,其中包含"Wheel"对象。由于汽车不应该没有轮子(至少根据我们的业务逻辑),为了构造汽车,在适当的领域驱动设计中这是有效的:
double radius = 17.0;
List<Wheel> carWheels = new List<Wheel>();
carWheels.add(new Wheel(radius));
Car aCar = new Car(carWheels);
我的问题基本上是,在聚合根之外实例化值对象以构造聚合根(在构造函数中传递值对象)是否良好。我不想在无效状态下创建聚合根,并希望遵循最佳实践。如果上面的代码不是最佳实践,应该怎么做呢?
我认为这既不是一个坏的做法也不是一个好的做法。这一切都取决于你想要建模的实际领域。在某些情况下,在聚合之外创建这些vo可能是有意义的,而在其他情况下,它只会打开您的域以供恶意使用。DDD迫使您忘记一些技术问题和坏/好的实践,以便专注于实际的领域:
- 在任何情况下,创造一辆26个轮子的汽车都有意义吗?您的示例模型允许
- 在任何情况下,创造一辆有四个轮子的汽车,每个轮子都有不同的半径,这有意义吗?您的示例模型允许
- 创建一个半径为17.3284546的车轮有意义吗?同样,你的模型允许这种情况发生
因此,我认为对于您所展示的示例,在聚合本身内部处理这些不变量可能更好,因为您可以很好地约束事物的数量,而不是在创建car和wheels时对它们进行约束。然而,这来自于对领域本身的更仔细的观察,而不是依赖于众所周知的好或坏的实践。重申一下,在某些情况下,在聚合之外创建VOs会更好。
我喜欢这个方法。在测试用例中,我们可以向Car注入存根/模拟Wheels。
如果在车轮或汽车的构造中存在复杂的业务约束,我会引入CarFactory。
在这个具体的例子中,我将给Car的构造函数提供半径,并让它自己构建车轮。这样,您就可以对客户端代码隐藏实例化细节(聚合之外的信息较少),并且您的客户端不会受到Car聚合的内部更改的影响。
您应该更喜欢在一个步骤/操作/操作中构建聚合。如果这不仅仅是一个步骤,那么您的聚合将不可避免地显示其内部结构的一些细节。