域模型定义a.o.实体之间的关系,我们定义聚合根来提供封装和事务边界。众所周知的关系是一对一关系(实体或值对象包含在聚合根中),一对多关系(聚合根包含子对象的集合)和多对多关系。后者很难,因为聚合根之间的多对多关系会使您遇到事务边界问题。因此,在许多情况下,多对多关系的一个方向被认为更重要,只有这种关系被建模为一对多关系。
现在,更进一步。 网络。等效合作伙伴之间的多对多关系。 如何在不违反聚合根的交易边界的情况下对其进行建模?
看看这个广泛适用的例子:
我有一个带节点的网络。每个节点都有有限数量的端口。一个端口只能连接到另一个节点上的一个端口。我必须能够使用端口添加和删除节点之间的连接
一种直观的方法是将节点建模为包含端口的聚合根。连接似乎是值对象,一个端口可以有一个连接。我可以实现一个Node.ConnectTo(nodeId,portId)方法,它将连接(节点A上的端口X和节点B上的端口Y之间)添加到聚合根节点A.最好,我会调用此方法两次,一次打开节点A和节点B上的一次,并将其包装在事务中。 但是,这会违反事务边界,因此我决定只将其存储在节点A上。
要在应用程序客户机上查看节点B上的连接,需要单独的读取模型。但这没问题,CQRS架构为我们提供了这些可能性。因此,添加,删除和查看连接不是问题
当我想在连接到端口之前验证端口是否仍然空闲时,会出现问题。尊重我们的事务边界的结果是(在写入模型中)端口已经连接的事实可能不为聚合根知道,但可能存储在任何其他聚合根中。
当然,您可以信任您的客户端验证,如果您正在添加它的节点可以添加连接,并依赖于运行一致性检查的进程来执行无效连接的补偿操作,请继续添加连接。但与围绕两个ConnectTo呼叫包装交易相比,这对我来说似乎是一件大事......
这让我觉得可能我的聚合根被错误选择。我开始将节点和网络视为聚合根,其中网络是连接的集合。网络聚合的好处在于您始终可以验证添加或删除连接。除非新连接会导致两个现有网络的加入......并且您的聚合可能会变大,可能只会导致一个庞大的网络。也不可行。
那么,您认为这应该如何建模? 您是否看到一个解决方案,您将聚合根视为交易边界,您可以验证您的网络,并且您没有风险将整个网络存储为单个聚合?或者我要求所有3个CAP在这里这根本不可能吗?
答案 0 :(得分:0)
好的,我read并且更多地考虑了它,我想这是'正确'的方法:
答案 1 :(得分:0)
我认为你的“新方式”是有缺陷的,因为View模型不应该产生一个“以某种方式”传播回域模型的异常。域模型需要自己解决这个问题。
因此,在这种情况下(绑定1对1),您可以利用域模型中的事件,以便
NodeA.connect(“port1”).to(NodeB).on(“port3”);
NodeA自己保留“port1”。
NodeA向NodeB发送“portConnectionRequest”。
NodeB绑定“port3”(如果可用)。
NodeB发送“portConnectionConfirmed”或“portConnectionDenied”。
NodeA接收事件并采取相应行动。
上面假设了可靠的消息传递,这在JVM中很容易实现,但在分布式环境中要难得多,但这就是你想要的更多。如果无法提供可靠的消息传递系统,我认为您手头有Byzantine Agreement Problem个问题或其中的一部分。