【发布时间】:2011-11-25 14:04:47
【问题描述】:
领域模型定义 a.o.实体之间的关系,我们定义聚合根以提供封装和事务边界。众所周知的关系是一对一关系(实体或值对象包含在聚合根中)、一对多关系(聚合根包含子对象的集合)和多对多关系。后者很困难,因为聚合根之间的多对多关系会使您在事务边界方面遇到麻烦。因此,在许多情况下,多对多关系的一个方向被视为更重要,并且只有该关系被建模为一对多关系。
现在,更进一步。 网络。 同等合作伙伴之间的多对多关系。 如何在不违反聚合根的事务边界的情况下对其进行建模?
看看这个应用广泛的例子:
我有一个带节点的网络。每个节点都有有限数量的端口。一个端口只能连接到另一个节点上的一个端口。我必须能够使用端口添加和删除节点之间的连接。
一种直观的方法是将节点建模为包含端口的聚合根。连接似乎是价值对象,一个端口可以有一个连接。我可以实现一个 Node.ConnectTo(nodeId, portId) 方法,它将连接(在节点 A 上的端口 X 和节点 B 上的端口 Y 之间)添加到聚合根节点 A。最好我会调用这个方法两次,一次节点 A 和节点 B 上一次并将其包装在事务中。 但是,这会违反事务边界,因此我决定仅将其存储在节点 A 上。
要在应用程序客户端上查看节点 B 上的连接,需要一个单独的读取模型。但这没问题,CQRS 架构为我们提供了这些可能性。因此,添加、删除和查看连接不是问题。
当我想在将连接添加到端口之前验证端口是否仍然空闲时,就会出现问题。尊重我们的事务边界的结果是(在写入模型中)聚合根可能不知道端口已经连接的事实,但可能存储在任何其他聚合根中。
当然,您可以信任客户端的验证,继续添加连接,如果您将其添加到的节点可以,并依靠运行一致性检查的进程来执行无效连接的补偿操作。但与围绕两个 ConnectTo 调用包装事务相比,这对我来说似乎很重要......
这让我觉得可能我的聚合根选择不正确。我开始将节点和网络视为聚合根,其中网络是连接的集合。网络聚合的好处是您始终可以验证添加或删除连接。除非一个新的连接会导致两个现有网络的连接......而且您的聚合可能会变得很大,可能只会导致一个巨大的网络。也不可行。
那么,您认为这应该如何建模呢? 您是否看到将聚合根视为事务边界的解决方案,您可以验证您的网络,并且您不会冒险将整个网络存储为单个聚合? 或者我在这里要求所有 3 个 CAP这根本不可能吗?
【问题讨论】:
-
你需要让你的问题更具可读性
-
查看FAQ How to ask 并使用级别格式来帮助我们阅读和理解您的问题。
-
您关于事务边界的推理似乎有点人为...... 看看系统中的角色可能会很好,谁将同时操作网络和节点,以及每个角色需要反馈的速度(也许整个事情可以离线和按顺序进行验证),或者切换到更面向预留的系统(例如锁定节点一段时间和某个操作)。不要忘记与领域专家交谈。
-
是的,伊夫,你是对的。这里写的讨论是非常人为的。它从关系(一对多、多对多)开始,而不是从一致性边界开始。聚合是关于一致性,而不是聚合,聚合是创建一致性边界所需的封装的结果。一致性要求确实由模型中发现的关系以外的其他因素决定。这就是您需要领域专家的地方。也许聚合应该被称为“一致性单元”,让人们首先想到一致性,而不仅仅是父子关系......
标签: validation transactions domain-driven-design aggregateroot