【问题标题】:DDD: How to handle large collectionsDDD:如何处理大型集合
【发布时间】:2014-09-18 14:27:41
【问题描述】:

我目前正在为 REST 中的社交网络相关应用程序设计一个后端。我对 DDD 原则非常感兴趣。现在让我们假设我有一个拥有朋友集合的用户对象。如果应用程序和用户变得非常成功,这些可能会达到数千。每个朋友也会有一些属性,它基本上是一个用户。 查看DDD Cargo application example,不时地从 CargoRepository 存储和检索完全扩展的 Cargo-object。哇,如果聚合根中有一个列表,随着时间的推移,这最终会触发 OOM。如果您从以数据为中心的角度来处理问题,这就是为什么会有分页和延迟加载的原因。但是,您如何在不知道持久性的 DDD 中处理这些大型集合?

【问题讨论】:

  • 为什么朋友在聚合根中,他们是你需要保护的不变量的一部分吗?
  • 他们是用户的一部分,因为用户有朋友,但用户也发布了消息,是群组的一部分,有宠物等(用户几乎是一切的整体根)。
  • 这听起来更像是一个关系模型而不是聚合根。 “有效的聚合设计”有一些很好的建议:dddcommunity.org/library/vernon_2011
  • 谢谢@JefClaes,这真的让我明白了。

标签: domain-driven-design ddd-repositories


【解决方案1】:

正如@JefClaes 在 cmets 中提到的:您需要确定您的 User AR 是否确实需要 Friends 的集合。

所有权并不一定意味着收藏是必要的。

Order / OrderLine 为例。如果不是Order一部分OrderLine 就没有任何意义。但是,Order 所属的Customer 没有Orders 的集合。如果客户被限制为最大数量(或数量)的 iro 活动订单,它可能会收集 ActiveOrders。保留历史订单的集合是不必要的。

我怀疑大型收集问题不仅限于 DDD。如果收到包含数千行代码的Order,则可能需要在设计上进行权衡,但订单更有可能被简单地拆分为更小的订单。

在您的情况下,我会断言 Friend 的包含/排除与 User AR 的一致性几乎没有关系。

要记住的是,一旦您开始使用域模型进行查询,就会遇到各种奇怪的问题。因此,请始终尝试考虑一些具有简单查询接口的读取/查询模型,该接口可以直接访问您的数据,而无需使用您的域模型。这可能会简化事情。

所以也许Relationship AR 可能会在这方面有所帮助。

【讨论】:

  • 您提出的“关系”正是我在阅读 cmets 中提到的 @JefClaes 网站上的 PDF 后得出的结论。太棒了:)
  • 我不太喜欢关系,因为它在 UL 中没有什么意义,至少从我的角度来看是这样。 UserFriends 可能是一个更好的名字。但可以肯定的是,一个单独的集合。
【解决方案2】:

如果某些分页或优化技术是您的领域的一部分,那么设计具有这种能力的领域类并没有错。

我想到的一些解决方案

  1. 如果用户是聚合根,您可以使用封装特定用户对象构造的方法GetUserWithFriends(int userId, int firstFriendNo, int lastFriendNo) 填充您的UserRepository。同样,您还可以使用一些计数器等填充用户模型。
  2. 另一方面,可以为User 实例的_friends 字段实现延迟加载。因此,User 实例可以自行决定加载好友列表的“部分”。
  3. 最后,您可以使用UserRepository 来获取某个用户的所有好友关于分页或其他过滤条件。它不违反任何 DDD 原则。

DDD 太大了,不能说它不适合 CRUD。以 DDD 方式进行编程时,您应该始终考虑一些技术限制并调整您的域以满足它们。

【讨论】:

  • 可以从用户域对象调用 UserRepository 吗?
  • 我没有说它是必需的。延迟加载最好通过代理对象 (stackoverflow.com/questions/25829172/…) 实现。一般来说,在域对象中使用存储库是一种罕见的情况,但我认为这没有什么问题。
  • 多一点想法。如果用户是聚合根,因此您有存储库来列出用户实例。但是用户的朋友也是用户。因此,您可以通过获取好友列表中包含 John 的所有用户来获取 John 的好友。但没有得到约翰和他所有的朋友。你看得到差别吗?前一种方法允许您以“UserRepository.GetAllUser(hasFriend="John", some_paging_params)”之类的方式从控制器中使用 UserRepository
  • 但是,如果您想要获取不是聚合根的用户帖子(如果是)而不是朋友,这将不起作用。根据我回答的第一句话,您还可以定义诸如 UserSlice 域类之类的东西,以封装数据以进行正确呈现。
  • 另外,在用户对象中使用存储库需要注入(依赖),我认为应该避免。
【解决方案3】:

不要过早优化。如果您害怕压力过大,那么您必须对您的应用程序进行基准测试并执行压力测试。

你需要有一个这样的表:

friends
id, user_id1, user_id2

处理 n-m 关系。在那里索引您的字段。

另外,你需要注意朋友是否对称。如果是这样,那么如果他们是朋友,那么您需要两个人的单排。如果不是,那么您可能只有一行,显示一个用户与另一个用户是朋友。如果其他人也将第一个视为朋友,则您需要另一行。

延迟加载可以通过隐藏 (AJAX) 请求来实现,因此用户会觉得它比实际速度更快。不过,我暂时不会担心这些问题,因为稍后您可以将表格的内容迁移到一个新的结构中,由于您的项目可能无限演进,该结构现在是未知的。

【讨论】:

  • 谢谢,我不同意这是早期优化,恕我直言,每次在集合中存储/加载您的 1000 多个朋友是一个设计缺陷。同样,问题不是关于我的用户用户用例,而是关于 DDD 如何对大数据集建模的更通用的问题。 DDD 似乎对于相对较小的 DomainObjects 是可行的,但如果你更喜欢 CRUD(比如我的情况)和大型列表,那么似乎缺少一些东西。
  • 你是说 DDD 对于大多数 REST API 来说没用?
【解决方案4】:

您的聚合根可以包含不同对象的集合,这些对象仅包含一小部分信息,作为对实际业务对象的引用。然后在需要时,可以使用项目从底层存储库中获取全部信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-14
    • 2021-11-14
    相关资源
    最近更新 更多