DDD和同质的多对多关系

时间:2013-02-08 01:17:36

标签: domain-driven-design

想象一下,你建立了一个社交网络。有些用户可以将其他用户添加为朋友。你如何在DDD中建模?当然,你不能简单地在User类中有一个朋友列表,因为任何朋友循环都会导致从存储库中获取这样一个用户的无限递归。

如果您需要跟踪可能待处理,已取消,已接受或已拒绝的好友请求,您将如何更改模型?

3 个答案:

答案 0 :(得分:3)

嗯...实际上你很容易做到你所问的,你的情况是标准的。您不必将实际的User对象存储在Friendslist汇总的User中,只需将User的朋友用户ID放在其中。

这是Vaugh Vernon提出的汇总实施规则之一:通过ID识别其他汇总和实体的链接。所以没有循环,只有ID列表。

在那种情况下,有人成为某人的朋友,你必须一次更改两个聚合。这可能是不受欢迎的行为,因为在一次交易中不能立即发生变化。但是对于这种情况,您可以轻松建模域事件和好友请求:您的聚合可以通过FriendshipRequestedFriendshipAcceptedFriendshipCancelledFriendshipDeclined事件相互通信并进行更改他们的状态相应。

在这种情况下,您还可以免费接收日志和通知。

答案 1 :(得分:1)

User可以包含Friend个列表。 Friend可以包含UserId,FriendType,GroupId等。

User也可以包含FriendRequest个列表。 FriendRequest可以有UserId,RequestMessage,RequestStatus等。

UserFriendFriendRequest都可以属于同一个聚合。但这样做可能会有一些重复。另一种选择是为所涉及的两个用户分配一个FriendRequest,每个用户都有一个已接收和发送的FriendRequest ID列表。

这只是为了给你一些想法,因为在DDD中,你的设计将高度依赖你的应用程序如何工作,以及需要哪些功能。 ; - )

答案 2 :(得分:1)

这在很大程度上取决于您的一致性边界需要在哪里。因此,这也取决于您拥有的业务规则。

虽然Meta-Knight在同一个Aggregate中有FriendRequest,但我会把它作为自己的,并使用事件在聚合之间进行通信,从而使Person和FriendRequests最终保持一致。这可以让你做类似的事情。

public class DomainRouter {
    public void When(FriendRequestCreated event)
    {
            //Send command to each Person to record request
    }

    public void When(FriendRequestAccepted event)
    {
            //Send command to Person to record request accepted and add friend.
            //Send comamnd to Person who accepted to add Friend
    }

    public void When(FriendRequestDeclined event)
    {
            //Send command to update Friend request on person.
            //Send command to Person who declined to record they have declined?
    }
}

因此,关于人的信息只是一个国家记录。 FriendRequest Aggregate将是所有进程实际发生的地方。

DDD的重要之处在于考虑行为。可以请求,撤回,接受和拒绝FriendRequest。一个人可以做什么?一个人是否需要成为DDDd,或者你是否可以将其变得简单,只需将CRUD +存储信息存入图形数据库等。

也许你想以一种你可以去person.requestFriendAcceptance(Person id)的方式对它进行建模,在这种情况下,路由器可能只是通过通知FriendRequest的朋友来处理FriendRequestCreated事件。