在哪里实现聚合根实体访问器方法的数据访问

Where to implement data access for an aggregate root entity accessor method

我有一个名为 Account 的聚合根和一个名为 Contact 的实体,可以通过根上的方法访问:Account.GetContactById(string id)。对聚合根的访问是通过存储库进行的,因此从存储中获取帐户的数据访问逻辑驻留在那里。

用于访问 Contact 实体的数据访问逻辑应该驻留在何处?我看到的大多数示例都会显示 Account.GetContactById 方法搜索内存中的集合。就我而言,Account 可以引用数千个 Contacts,我不想将其预取到内存中。因此,鉴于调用该方法时需要访问数据存储,我是否在以下位置实现该访问:

  1. Account.GetContactById 方法?这将传播对存储库外部存储的直接访问并引入一些紧密耦合。
  2. AccountRepository,所以它可以被Account聚合调用?这似乎将 Contact 实体直接暴露给存储库的任何其他用户,这违反了 Evans 的规则。
  3. 另一个存储库,例如ContactRepository?在那种情况下,我有一个不是聚合根的实体的存储库。
  4. 其他?

@plalx 的评论为我指明了正确的方向。我在这里发布我的解决方案作为答案,以帮助可能有相同类型问题的其他人。

阅读 Vernon 撰写的几篇关于聚合建模的非常好的文章(您可以找到这些文章 here and here)后,我得出的结论是,我让组合结构驱使我走向一个糟糕的模型。仅仅因为 ContactAccount 相关不足以将它们放在同一个集合中。来自弗农:

designing aggregates is more about consistency boundaries. A reference between two aggregates does not mean they are in the same consistency boundary, and therefore a single aggregate with one as the root.

他以 sprint 跟踪系统为例,解释了这是如何无法扩展的,部分原因是我遇到的问题:

Keeping performance and scalability in mind, what happens when one user of one tenant wants to add a single backlog item to a product, one that is years old and already has thousands of backlog items? Assume a persistence mechanism capable of lazy loading (Hibernate). We almost never load all backlog items, releases, and sprints all at once. Still, thousands of backlog items would be loaded into memory just to add one new element to the already large collection.

他就如何在可能的情况下首选具有值类型的单个实体聚合而不是我正在构建的大型集群多实体聚合模型提出了一些有用的评论,并且单独的聚合应该通过标识符相互引用只要。他建议将应用程序服务作为解决聚合之间关联的一种方式。

所以,最后,我把ContactAccount分开成不同的聚合体,用一个应用服务来解析关联。