我可以更改汽车制造厂的签名以接受完整的驾驶员实体。然后,工厂将仅从该实体中选择ID,并以此为单位构建汽车。这里不变量被隐式检查。
这种方法很吸引人,因为您可以免费获得支票,并且与普遍使用的语言保持一致。A Car
不是由a驱动driverId
,而是由a 驱动Driver
。
沃恩·弗农(Vaughn Vernon)实际上在其Identity&Access示例的有界上下文中使用了这种方法,在该上下文中,他将一个User
聚合传递到一个Group
聚合,但Group
仅适用于值类型GroupMember
。如您所见,这还使他能够检查用户的启用情况(我们很清楚该检查可能已过时)。
public void addUser(User aUser) {
//original code omitted
this.assertArgumentTrue(aUser.isEnabled(), "User is not enabled.");
if (this.groupMembers().add(aUser.toGroupMember()) && !this.isInternalGroup()) {
//original code omitted
}
}
然而,通过传递Driver
情况下,你还开自己给的意外修改Driver
中Car
。传递值引用使从程序员的角度更容易推理出变化,但与此同时,DDD与泛在语言有关,因此值得冒险。
如果您实际上可以拿出好名字来应用接口隔离原则(ISP),那么您可能依赖于没有行为方法的接口。您也许还可以提出一个值对象概念,该概念表示不可变的驱动程序引用,并且只能从现有驱动程序(例如DriverDescriptor driver = driver.descriptor()
)实例化。
我可以想象那些聚合可能生活在单独的有界上下文中,现在我将依赖于DriverBC的DriverRepository或Driver实体污染汽车BC。
不,您实际上不会。总会有一个反腐败层,以确保一个上下文的概念不会渗入另一个上下文。它实际上是容易得多,如果你有一个专门的汽车司机协会BC,因为你可以模拟现有的概念,如Car
和Driver
专门用于这方面。
因此,您可能DriverLookupService
在BC中有一个负责管理汽车驾驶员关联的定义。该服务可以调用由驱动程序管理上下文公开的Web服务,该服务返回Driver
在该上下文中很可能是值对象的实例。
请注意,Web服务不一定是BC之间最好的整合方法。您还可以依赖消息传递,例如,UserCreated
来自驱动程序管理上下文的消息将在远程上下文中使用,该消息会将驱动程序的表示形式存储在其自己的数据库中。DriverLookupService
然后,他们可以使用该数据库,并且驱动程序的数据将与其他消息(例如DriverLicenceRevoked
)保持最新。
我无法真正告诉您哪种方法更适合您的领域,但是希望这将为您提供足够的见识以做出决定。
Driver.delete
不应该存在。我从未真正看到过聚集体被破坏的领域。通过保持AR周围,您永远不会成为孤儿。