DDD 考虑实体的存储库和服务

DDD considering Repositories & Services for entities

我一直在熟悉 DDD 并试图了解 EntitiesAggregate Roots 交互的方式。

情况举例如下:

假设有一个用户 he/she 有多个电子邮件地址(为了举例,最多可以有 200 个)。每个电子邮件地址都有自己的身份,用户也是如此。并且用户和他们的电子邮件之间存在 one to many 关系。


在上面的例子中,我认为 UsersEmails 是两个实体,而 Usersaggregate root

我遇到的DDD规则:

问题 1: 这是否意味着我不能有一个单独的数据库 table/collection 来单独存储电子邮件?这意味着电子邮件必须嵌入到用户文档中。

问题 2: 现在考虑到我将它们分成两个不同的 tables/collection 和 link 电子邮件,方法是在电子邮件中有一个名为 associatedUserId 包含对电子邮件所属用户的引用。我不能直接拥有像 /users/{userId}/emails 这样的 API 端点并直接在 EmailService.getEmailsByUserId(String userId) 中处理它吗?如果不是,我该如何建模?

如果这个问题看起来有点太幼稚,我很抱歉,但我似乎无法弄清楚。

Only aggregate root has access to the repository

Does it mean that I cannot have a separate database table/collection to store the emails separately? Meaning that the emails have to be embedded inside the user document.

这意味着如果您要对聚合的任何成员实体进行任何更改,则应该获取一个锁。这当然意味着聚合的数据表示存储在单个数据库中;但您当然可以将信息分布到该数据库中的多个表中。

回到 2003 年,使用关系数据库作为记录簿很普遍;一对多关系通常涉及同一个数据库中的多个表。

Entities outside the aggregate can only access other entities in the aggregate via the aggregate root.

I can't directly have an API endpoint like /users/{userId}/emails and handle it directly in the EmailService.getEmailsByUserId(String userId)?

当然可以;为此,您将首先加载用户聚合的根实体,然后调用该实体的方法以获取您需要的信息。

一个观点:Evans 反对应用程序应该能够直接操纵领域模型中的任意 实体的想法。相反,应用程序应该只被允许访问域模型中的“根”实体。该限制实际上意味着应用程序并不真正需要了解多个实体共享的约束。

四五年后出现,进一步提炼了这个想法——事实证明,在只读用例中,领域模型不一定有很大贡献;如果不变量已经满足并且您没有更改任何内容,则无需担心不变量。

实际上,这表明 GET /users/{userId}/emails 可以只将数据从只读视图中拉出,根本不需要涉及域模型。但是POST /users/{userId}/emails需要展示原始关怀(意思是,我们需要通过领域模型修改数据)

does this mean that I need to first go to the UserRepo and pull out the user and then pull out the emails, can't I just make a EmailService talking to an Email Repo directly

在 Evans 的原文中,存储库允许访问 根实体 ,而不是任意实体。因此,如果“电子邮件”是“用户聚合”中的一个实体,那么它通常不会有自己的存储库。

此外,如果您发现自己反对那个想法,它可能是一种“代码气味”,试图让您认识到您的聚合边界位于错误的位置。如果电子邮件和用户处于不同的集合中,那么在 课程 中,您将使用不同的存储库来获取它们。

诀窍在于认识到聚合设计反映了我们如何锁定数据以供修改,而不是我们如何 link 报告数据。