网络:如何使用聚合根建模?

时间:2011-11-25 14:04:48

标签: validation transactions domain-driven-design aggregateroot

域模型定义a.o.实体之间的关系,我们定义聚合根来提供封装和事务边界。众所周知的关系是一对一关系(实体或值对象包含在聚合根中),一对多关系(聚合根包含子对象的集合)和多对多关系。后者很难,因为聚合根之间的多对多关系会使您遇到事务边界问题。因此,在许多情况下,多对多关系的一个方向被认为更重要,只有这种关系被建模为一对多关系。
现在,更进一步。 网络。等效合作伙伴之间的多对多关系。 如何在不违反聚合根的交易边界的情况下对其进行建模?
看看这个广泛适用的例子:
我有一个带节点的网络。每个节点都有有限数量的端口。一个端口只能连接到另一个节点上的一个端口。我必须能够使用端口添加和删除节点之间的连接 一种直观的方法是将节点建模为包含端口的聚合根。连接似乎是值对象,一个端口可以有一个连接。我可以实现一个Node.ConnectTo(nodeId,portId)方法,它将连接(节点A上的端口X和节点B上的端口Y之间)添加到聚合根节点A.最好,我会调用此方法两次,一次打开节点A和节点B上的一次,并将其包装在事务中。 但是,这会违反事务边界,因此我决定只将其存储在节点A上。
要在应用程序客户机上查看节点B上的连接,需要单独的读取模型。但这没问题,CQRS架构为我们提供了这些可能性。因此,添加,删除和查看连接不是问题 当我想在连接到端口之前验证端口是否仍然空闲时,会出现问题。尊重我们的事务边界的结果是(在写入模型中)端口已经连接的事实可能不为聚合根知道,但可能存储在任何其他聚合根中。 当然,您可以信任您的客户端验证,如果您正在添加它的节点可以添加连接,并依赖于运行一致性检查的进程来执行无效连接的补偿操作,请继续添加连接。但与围绕两个ConnectTo呼叫包装交易相比,这对我来说似乎是一件大事...... 这让我觉得可能我的聚合根被错误选择。我开始将节点和网络视为聚合根,其中网络是连接的集合。网络聚合的好处在于您始终可以验证添加或删除连接。除非新连接会导致两个现有网络的加入......并且您的聚合可能会变大,可能只会导致一个庞大的网络。也不可行。
那么,您认为这应该如何建模? 您是否看到一个解决方案,您将聚合根视为交易边界,您可以验证您的网络,并且您没有风险将整个网络存储为单个聚合?或者我要求所有3个CAP在这里这根本不可能吗?

2 个答案:

答案 0 :(得分:0)

好的,我read并且更多地考虑了它,我想这是'正确'的方法:

  1. 在节点A上执行ConnectTo方法之前,使用最终一致的视图模型作为数据源验证节点B上的端口是否仍然是空闲的(不是无法有效验证此模型的域模型,请参见上文)。 LI>
  2. ConnectTo仅在节点A上运行,因此不会违反事务边界。
  3. 如果视图模型无法连接节点B上的端口,因为它已在使用中,因此发生了真正的并发异常,必须发出信号。需要采取一些行动(人工干预或自动化流程必须采取行动)。此并发异常的概率通常非常低。

答案 1 :(得分:0)

我认为你的“新方式”是有缺陷的,因为View模型不应该产生一个“以某种方式”传播回域模型的异常。域模型需要自己解决这个问题。

因此,在这种情况下(绑定1对1),您可以利用域模型中的事件,以便

  1. NodeA.connect(“port1”).to(NodeB).on(“port3”);

  2. NodeA自己保留“port1”。

  3. NodeA向NodeB发送“portConnectionRequest”。

  4. NodeB绑定“port3”(如果可用)。

  5. NodeB发送“portConnectionConfirmed”或“portConnectionDenied”。

  6. NodeA接收事件并采取相应行动。

  7. 上面假设了可靠的消息传递,这在JVM中很容易实现,但在分布式环境中要难得多,但这就是你想要的更多。如果无法提供可靠的消息传递系统,我认为您手头有Byzantine Agreement Problem个问题或其中的一部分。