【问题标题】:Networks: how to model them using aggregate roots?网络:如何使用聚合根对其进行建模?
【发布时间】: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


【解决方案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 收到事件并采取相应措施。

以上假设可靠的消息传递,这在 JVM 中很容易实现,但在分布式环境中要困难得多,但这是您更想要的地方。如果无法提供可靠的消息传递系统,我认为您将遇到Byzantine Agreement Problem 问题,或者它的子集。

【讨论】:

  • 每当“需要交易”出现在您的脑海中时,就想一想“银行间汇款”。 “交易支持者”总是提出银行交易,但没有银行间 2 阶段提交,只有最终一致性。
  • 你是对的。域模型必须自己处理。我的解决方案很接近(从视图模型发出事件信号),但不像你的那样干净。事实上,BAP 可以通过超时保留来解决。不错!
【解决方案2】:

好的,我read 想了更多,我想这是“正确”的做法:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-05
    • 2016-03-17
    • 2013-04-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多