DDD - 执行需要了解多个聚合根的规则

问题描述 投票:5回答:4

我是DDD的新手,目前正在寻找重建现有的应用程序,从一些概念证明开始,而我仍然在寻找DDD的方法。我的问题只涉及领域模型的一小部分,所以它在某一刻似乎过于简单化了。

这是一个为在家中探望病人的护士安排的应用程序。因此,很明显“Patient”是一个AggregateRoot,而“Nurse”是另一个AggregateRoot。除了指派护士使用“预约”实体访问患者之外,患者与护士没有直接联系。

现在,约会实体可以很容易地属于患者或护士AR,或者甚至两者都认为预约是两者之间的联系。因此,我也将任命变为AR。所以第一个问题是:

1)这种建模听起来不错吗?我原本试图在患者/护士AR下添加一组约会实体,但由于它确实属于两者,因此它是自己的AR是有意义的。我当时正考虑在护士/病人AR下添加一个约会ID列表以链接到他们的约会,但这意味着保存约会的交易需要同时影响多个AR,这可以从我所知道的建议糟糕的聚合设计。

假设到目前为止这种建模是有意义的,我现在需要找出执行业务规则的最佳方法,这些规则涉及当前所有3个AR。例如,护士不能同时在一个以上的地方,因此我们不能与分配给同一护士的另一个同时创建预约。每位患者也只能预约一次。所以第二个问题是:

2)你将如何执行这些涉及多个不同AR的规则?显然,如果约会是患者或护士AR下的嵌套集合,则规则将易于实施,并且非常自包含。现在这让我怀疑我的建模是否正确。

我已经在不列颠哥伦比亚省和佐贺县/流程经理周围阅读了很多内容,但对我而言,这是同一个BC的一部分,因此不确定我是否需要任何复杂的东西。简单地使用CommandHandler来加载多个AR对象并使用它们的状态来确定是否可以创建约会是否可以接受?

如果是这样,并且与上面的Q1重叠(假设我没有在护士/患者AR下存储预约ID列表),阅读模型是轻松找到属于相应护士/患者的预约的唯一方法 - 那么基于读取模型的状态而不是存储库中的AR来强制执行业务规则也是可以接受的吗?

希望这是有道理的,并提前感谢!

domain-driven-design aggregateroot
4个回答
1
投票

这种建模听起来不错吗?

不(但这不是你的错 - 文学很糟糕)。您的聚合将成为信息的表示,而不是在现实世界中移动的人。轮换时间表,值班者,那些可能是聚合的东西。

例如:

一个护士不能同时在一个以上的地方,所以我们不能同时指定给同一个护士的另一个约会

这不是对护士的限制,这是对时间表的限制。

“上午9点,护士(身份证号码:12345)将访问病人(身份证号码:67890)”是一个时间表条目。完全可以直接管理所有计划条目。时间表的视图可能还需要包括有关护士或患者的其他信息,因此视图可以加入其他信息。

该计划成为其自己的“聚合”,使用相关ID来启用与其他信息的连接。

时间表是“NurseSchedule”还是系统范围的“时间表”?

这可能是调度护士的用例特有的。根据域名,给定的时间表可能跨越许多护士和患者。


0
投票

简单地使用CommandHandler来加载多个AR对象并使用它们的状态来确定是否可以创建约会是否可以接受?

不,如果你想遵循DDD方法,那就不行了。 Aggregate不应小于事务,Aggregate是事务边界。

从我看到你有这些业务不变量:

  1. 一个护士不能同时在一个以上的地方,所以我们不能同时指定给同一个护士的另一个约会
  2. 每位患者只能预约一次

只有当护士和患者属于同一聚合体时,才能强制执行这两个规则。也就是说,如果您希望无论如何都要尊重这两个规则,那么您应该有一个大的聚合。

但拥有如此庞大的聚合体可能会感觉不对。您可以做些什么:权衡:两个规则中的哪一个可以最终一致的方式实施?在与业务专家讨论并向他/她展示业务影响后,您选择一个,然后创建一个Saga /流程管理器,检测这种无效状态并更正它和/或通知某人手动更正它。

如果是这样,并且与上面的Q1重叠(假设我没有在护士/患者AR下存储预约ID列表),阅读模型是轻松找到属于相应护士/患者的预约的唯一方法 - 那么基于读取模型的状态而不是存储库中的AR来强制执行业务规则也是可以接受的吗?

Saga / Process管理器使用旧数据(最终一致的更新数据)将正确的命令发送到聚合,就像Readmodels一样。因此,您可以让Saga维护一个私有状态(更安全/更清洁的解决方案),或者让查询规范的Readmodel来查找无效的情况(更快/更脏的解决方案)。


0
投票

聚合根定义了一致性边界,该边界不必是数据库上的单个事务。是否存在单个或多个事务的问题是性能/基础结构问题,应该在存储库的实现中解决,而不是在域模型中解决。甚至不能指望域模型的聚合根和持久性模型是相同的。如果您的域名要求在所有访问中保持一致性并且您必须防止任何冲突,那么建立一个知道所有护士和患者并实施所有一致性规则的单一聚合(让我们称之为“计划”)。

计划存储库的一种可能实现是仅为持久性创建护士和患者实体。检索计划聚合意味着加载所有护士和患者实体,一些缓存策略可能会提高此处的性能。存储修改后的Schedule聚合将涉及修改后的Nurse和Patient实体的乐观并发,因此只能减少版本冲突。正如您已经注意到Saga /流程经理似乎有点矫枉过正。我甚至会更进一步说,一个传奇会扩散域逻辑和初始意图。当然,如果域名在任何时候都没有强制执行所有访问的一致性,那么您将处理最终的一致性。然后,在域层中使用Nurse和Patient聚合并实现流程管理器是有意义的。


0
投票

如果你想要立即一致,唯一干净的方法是在域模型中有一个大的聚合。为避免不相关的乐观并发事件,您可以尝试在域存储库的实现中拆分聚合。

© www.soinside.com 2019 - 2024. All rights reserved.