【问题标题】:Should an aggregate root's behaviour be dependent on other aggregate root's attributes?聚合根的行为是否应该依赖于其他聚合根的属性?
【发布时间】:2014-01-15 20:37:51
【问题描述】:
我正在阅读一本关于 DDD 的书,我看到了一个涉及汽车、发动机、车轮和轮胎的示例域。
上面是书中的模型。客户也是聚合根。
有了这个模型,引擎可能会有高度、宽度和长度属性。
当您需要在小型汽车上安装大型发动机时会发生什么?引擎无法安装。
如果汽车检查引擎属性并允许它成为汽车的一部分,这是否有问题?
该引擎具有全球标识(就像您知道每个引擎都有一个序列号/制造商编号)。也许发动机需要由制造商跟踪。
所以我再次问,如果汽车使用引擎的属性将其装入内部(允许或不成为其中的一部分),这是否有问题?
【问题讨论】:
标签:
oop
architecture
domain-driven-design
aggregation
【解决方案1】:
来自 DDD 书,第 128 页:
任何跨越 AGGREGATES 的规则都不会一直是最新的。通过事件处理、批处理或其他更新机制,可以在特定时间内解决其他依赖关系。
所以,这真的取决于 Car 聚合的用途:如果它需要与 Engine 的强一致性,那么 Engine 应该是 Car 聚合的一部分。
否则,如果它只需要“最终一致性”,您可以将该验证逻辑放在域事件中。
查看这个 Udi Dahan 的 post
【解决方案2】:
如果汽车检查引擎属性并允许它成为汽车的一部分,是否有问题?
没有。
话虽如此,您的验证可能已经复杂到足以引入域服务。由于涉及到两个聚合,您可以这样:
car.Fit(engine)
或者这个:
engine.Fit(car)
但是,您可能还是想检查汽车型号:)
由于规则会更高级并涉及一些数据,您可能想要引入域服务并可能对对象使用双重调度:
所以你可以有这个而不是car.Fit(engine):
car.Fit(engine, IModelServiceImplementation)
并在Fit方法调用中:
if (!IModelServiceImplementation.CanFit(car, engine)) { throw new Exception(); }
该服务可能会加载正确的模型,而是根据引擎进行检查。根据领域的不同,甚至可能需要处理修改级别和其他规则。
由于Car 实例不包含实际的Engine 实例,而仅包含EngineId 或某些值对象,因此不会将引擎真正分配给汽车。您仍然可以将引擎实例传递给汽车并让它创建它认为合适的关联。
“Enrico S.”提出的解决方案可能与更改影响聚合根的场景更相关,在这种情况下,可能没有所有聚合根可用,甚至聚合根存在于单独的有界上下文中。即使Car 和Engine 位于不同的BC 中,也可能能够以某种方式查询有效性。有些事情对于最终的一致性来说是可以的,但有些事情可能不是。
像往常一样,有很多事情要考虑:)