我一直在阅读Evans关于DDD的书,并且正在考虑如何在.NET中实现聚合。目前,我只能想出一个方法;在单独的类库中隔离聚合。然而,这似乎有点过分(我宁愿将所有域对象保留在一个库中),我想知道是否有不同的方法?
1 lib / aggregate的原因如下:聚合根需要知道它负责对“子对象”的所有访问,聚合根也可以返回子对象作为其成员的结果。因此,这些子对象的成员(聚合根所需)不能公开。因此,您唯一的选择是将它们设置为内部(因为它们仍然需要由聚合根调用)。但是,通过将所有聚合放在一个项目中,仍然可以从已获取子对象的其他域对象访问这些成员。这是不合需要的,因为它允许绕过聚合根。通过分离不同库中的所有聚合,这个问题就解决了。
其他一些信息:
我已经检查了DDD java sample code,并将每个聚合(包括所有子对象的类)打包到另一个包中。只能从聚合根调用的成员没有访问修饰符(例如:Delivery.updateOnRouting
)。在java中,没有访问修饰符的成员是package-private(仅在同一个包中可用)。所以这将是正确的行为。
然而,.NET sample code将所有域对象放在一个类库中,然后将相应的成员公开。对我来说,这似乎是错误的。
答案 0 :(得分:6)
聚合是DDD中最难的概念之一。你有大部分是正确的。我建议用“成员资格”来表达这个概念,比引入“子对象”一词更为直接。
是的,对象不能是多个聚合的成员。哪一个是最终的执法者?通过删除成员和孤立其他聚合中的其他成员,一个聚合根可能很容易使另一个成为无效。你是对的,在一个对象似乎需要多个聚合的成员资格的情况下,该对象必须是一个独立的实体,即它成为新聚合的根。 (它可能有也可能没有其他成员,但如果没有,那么它当然会成为它自己的一个成员。)
是的,绝对存在强制存在不变量的聚合。就持久性而言,它也是单个工作单元或单个事务。聚合根最终负责整个成员的所有不变量,这必然是因为,例如,不变量的失败可能导致持久性失败,并且聚合负责将聚合维护为可行的单个持久性工作单元
然而,这是微妙而困难的部分,最终责任并不意味着聚合也是 主要 强制执行者。就像我们在司法系统中所拥有的一样 - 法院最终是确定法律问题的最终场所,并且在实施最终法治的地方,执行不变权。但实际的执行(和合规性)发生在系统的许多层面。事实上,在一个秩序井然的社会中,大多数强加法治的活动 - 不变量的执行 - 应该在你到达法庭之前就已经发生,你甚至不应该依赖常规去法院。 (尽管在DDD中,您可能总是希望聚合根在例如持久性之前进行最终的不变扫描。)
你所暗示的是完全不同的,基本上你的整个社会除了法庭被监禁,你似乎甚至提出其他人甚至不能访问,他们只能传递信息到法院并希望它行为恰当。
如果您按照建议的路径行进,请查看您的域名会发生什么。目标是创建一个丰富而富有表现力的领域模型。就有意义的无处不在的语言而言,您已将工作词汇量仅减少到聚合词根。由于不变量,聚合根应该访问实体,但也因为如果设计正确,则实体具有有意义的身份,该身份源于其在聚合根的上下文中的成员身份。但是你的提议实体在其聚合根之外甚至没有任何类型标识。 Evans特别说这是聚合根目的的一部分 - 允许对象通过遍历获取对成员的引用。但是您无法获得有用的引用,因为另一个对象甚至不知道您的成员类型存在。或者您可以更改名称空间,但如果您不允许遍历则不会更好。现在,您的整个域都知道类型,但无法获得的对象类型。
更糟糕的是你的聚合根发生了什么。除了维护聚合完整性之外,聚合根通常应该有它自己存在的理由。但现在这种身份已经不复存在了。需要为所有各种元素及其属性提供包装方法,这一点变得模糊不清。你得到的是不再具有表现力甚至是明确身份的聚合根,只是大而笨重的上帝对象。
Order和OrderLine的例子就是一个有趣的例子。订单未提供OrderLine代表OrderLine所需的某些不变量的强制执行。在这种情况下控制动作来强制执行它自己的不变量。这是对聚合根进行离子控制的有效操作。然而,更典型的聚合主要涉及对象创建和/或破坏。
当然没有要求强加模型,其中状态的所有更改必须由聚合根自动应用,而不是直接应用。实际上,这通常是聚合根允许遍历获取成员引用的原因 - 因此外部对象可以应用状态更改,但是在聚合控制要更改的成员实体的实例生命周期的上下文中。
不仅是可见性,而且与更大域的实际交互通常是开发丰富且富有表现力的模型的基础。聚合用于控制访问,但不能完全消除它。
我希望这有帮助,这是一个难以讨论的概念。
答案 1 :(得分:2)
我只能想出一个方法; 将聚集体分开分离 班级图书馆。然而,这似乎是 有点矫枉过正
更像是很多的过度杀伤力。做这样的事情的开销是残酷的,你做不想要数十个项目,这个方法在任何非平凡的应用程序中创建。
答案 2 :(得分:2)
Therefore, members (needed by the aggregate root) of these sub-objects can't be made public.
我认为这个结论过于刻板而不实用,并不是埃文斯所倡导的。是的,他确实说Agg Root负责创建和访问该Root中的其他对象,但是
首先,聚合根倾向于重叠。如果在两个不同的根中需要一个小部件,无论出于何种原因,它都是必需的。所以你真的不能让它(在这种情况下是小部件)可用于一个Root而不是另一个。
其次,我认为(我的解释和我没有出书!)对于对象访问而言,Agg Root的想法更像是一种惯例,而不是教条。该教条是在该根的上下文中满足客户端请求以简化域。它更多的是为Aggregate Root开发接口,然后让客户端通过该接口来满足他们的需求。如果您可以限制访问(任何)客户端不需要的对象(使用任何聚合根),那么一定要这样做。
HTH,
Berryl
答案 3 :(得分:2)
除了你的问题的(有趣的)理论方面,我认为你的问题的一个实际答案是使用命名空间来分离聚合。在.NET中,您将类放在名称空间中。命名空间结构独立于项目结构,您可以将多个命名空间放在单个项目中,在编译时会在单个程序集中生成。
您可以使用它将所有域类放在一个程序集中,并且它们之间仍然存在分离。
答案 4 :(得分:0)
我认为您不希望为其他程序集隐藏“子对象”的类定义。您可能无法从其他聚合根引用它们,但如果要基于这些子对象创建表示模型,该怎么办?您需要从其他程序集访问它们。
因此,从DDD的角度来看,您可能是正确的,但我认为您不希望您的代码将此反映到您建议的极端。这将成为非常不切实际的代码。