使用域驱动设计建模一对多关系

时间:2017-08-21 20:15:44

标签: domain-driven-design

这个问题更多的是关于如何使用集合建模简单的一对多关系的一般性问题:列表项中的更改是否应该反映在包含它的聚合版本中?

域名是会议日程安排(就像在Outlook中一样)。 我有一个会议实体,可以有多个参与者。 参与者可以接受/拒绝会议请求。 重新安排会议将使所有与会者确认无效。

我想到了两种模拟方法。

选项1 会议聚合将包含参与者列表,其中每个参与者具有ParticipantId和状态(接受/拒绝)。 这里的问题是,对于特定参与者,每个Accept或Deny命令都会增加Meeting的版本,这意味着如果尝试接受基于相同原始版本的会议请求,则两个参与者将进入竞争条件。 虽然可以通过重新阅读会议文档并重试“接受”命令来解决这个问题,但考虑到这种情况发生的频率,这很烦人。 另一种方法是在执行Accept命令时忽略会议的版本,但这会引入一个新问题:如果在发送会议请求后会议已重新安排,会发生什么?在这种情况下,我们无法忽略Meeting的版本,因为这次版本DOES代表了应该考虑的真实版本。 顺便说一句,忽略某些命令中的版本而不是其他命令中的版本是不是很好?

选项2 从会议中提取参与聚合。 参与将有MeetingId,ParticipantId和Status。 它也有自己的版本。 这样,当参与者X接受会议请求时,仅修改相关参与,其余参数将保持不变。 并且,在重新安排会议时,会议重新安排"将发布事件,事件处理程序将通过重置所有参与来响应它。状态为" NotAccepted"不管他们目前的版本。 一方面,这听起来合乎逻辑,因为会议的版本不应仅仅因为某人接受/拒绝其请求而增加。 另一方面,将参与建模作为一个独立的聚合对我来说听起来并不合适,因为它在会议的背景之外没有任何意义。

无论如何,我希望得到有关此问题的反馈,并了解解决此问题的各种方法。

1 个答案:

答案 0 :(得分:1)

  

虽然可以通过重新阅读会议文档并重试“接受”命令来解决这个问题,但考虑到这种情况发生的频率,这很烦人。

这看起来像是一个建模错误。您应该记住,会议聚合不是参与者可用性的记录簿 - 现实世界是。所以消息不应该是AcceptInvitation,而是 InvitationAccepted 。关于这一点不应该是冲突,因为域模型不会否决其权限边界之外的事件。

根据您的实现,您可能会在管道中遇到并发修改异常,但这应该是您应该自动处理的事情(即:预期版本,或重试)。

  

另一种方法是在执行Accept命令时忽略会议的版本,但这会引入一个新问题:如果在发送会议请求后会议已重新安排,会发生什么?

这里的解决方案是更仔细地建模。是的,有时您会收到一条消息,接受或拒绝已过期的邀请。

换句话说:race conditions don't exist

  

时间上的微秒差异不应对核心业务行为产生影响。

当会议重新安排时,Alice立即回复了邀请,会发生什么?为什么鲍勃不会发生同样的事情,当他的回复在会议重新安排之后到来时?

  

作为一个独立的聚合参与并不适合我,因为它在会议背景之外没有任何意义。

我发现启发式并不是特别有效。了解实体是否可以独立改变状态,或者是否需要协调其变化,这一点要重要得多。

  

实际上,会议聚合用于跟踪参与者的可用性。它的目的是什么。除非我没有完全理解你......

它有点微妙,我没有把它拼出来。

假设模型说我可以使用,但现实世界中的紧急情况会让我失望。怎么了?我被阻止去医院,因为模特说我必须去参加一个会议吗?有人可以通过更改我提交的邀请来取消我的紧急情况吗?

此外,如果我在紧急情况下离开,您是否可以参加与您和我将要举行的会议同时举行的会议?

在这个空间中,现实世界是对某人是否可用的权威。该模型只是查看消息的缓存副本,描述过去是否有人可用。

模型使用的缓存信息不保证完整。请参阅warehouse systems and exception reports上的Greg Young。

  

这让我觉得会议聚合可能有两个版本字段:一个是强版本,当递增时表示突破性更改,另一个软版本表示非中断更改。这有什么意义吗?

不是真的。据我所知,版本并不是来自无处不在的安排会议语言的术语。它是元数据,如果它存在的话,模型中的业务规则不应该依赖于元数据。

  

我同意,但会议ID(或任何ID)也不是无处不在的语言的一部分,但我可能会在我的域名世界和外部世界之间来回传递。