【问题标题】:Is it okay to bypass the repository pattern for complex queries?是否可以绕过复杂查询的存储库模式?
【发布时间】:2011-11-09 15:20:37
【问题描述】:

这是我目前对DDD的理解:

  • 严格的存储库模式应该只实现 get()、delete() 和 create(),也许还有 get() 的变体,人们可以在其中搜索或检索整个集合
  • 每个聚合根通常都有一个存储库

(根据研究,我知道这些不是普遍接受的规范)

这里的问题是如何实现涉及许多聚合根的复杂查询。例如,我们有两个聚合根 - 产品和用户。如果我正在创建一个列出用户购买了哪些产品的页面,那么我就会有一个跨越用户聚合和产品聚合的查询。

这个查询应该如何实现?

  1. 我现在所做的实际上是为此查询和具有相关功能的查询建立一个存储库(有些人会不同意并说存储库不是查询层)。

  2. 仅使用产品和用户的存储库,获取所有记录并在内存中执行所有操作(这听起来不对)

  3. 让查询(LINQ 或 SQL)位于服务内部,根本不使用与聚合关联的存储库。

还有其他方法吗?

【问题讨论】:

    标签: design-patterns domain-driven-design repository


    【解决方案1】:

    只需将其放在存储库类中即可。它是一个 Get,可能在 ProductRepository 中,因为这就是它返回的内容。 GetProductsByUser(int UserID)。或者如果你有一个 n 层架构,那么它可以在服务方法中。

    我一直认为存储库是放置任何数据库访问方法的地方,因为这个想法是将数据库代码与其他所有内容分开。

    【讨论】:

      【解决方案2】:

      严格的存储库模式应该只实现get()、delete() 和 create(),也许还有 get() 的变体,人们可以在其中搜索或 检索整个集合

      存储库接口是您域的一部分,应尽可能基于Ubiquitous Language。所有存储库都不同,就像您的所有聚合不同一样。严格来说,generic repositories 是 CRUD 过度泛化,可能会降低代码的表达能力。 “创建”方法也不属于存储库,因为对象生命周期的开始通常由工厂或对象本身处理。当您想要保留现有对象时,“添加”似乎是一个更好的名称,因为 Repository 具有集合语义。

      这里的问题是如何实现复杂的查询,这涉及到 许多聚合根。例如,我们有两个聚合根 - 产品和用户。如果我正在做一个列出用户产品的页面 已经购买了,然后我有一个跨越两个用户的查询 聚合和产品聚合。

      在这种情况下你只需要倾听业务需求,我强调了我认为最重要的部分。基于此,您似乎需要:

      Products products = /* get Products repository implementation */;
      IList<Product> res = products.BoughtByUser(User user);
      

      这样组织代码的想法是尽可能地匹配业务需求和无处不在的语言。存储库接口的命名也很重要,我更喜欢使用 ProductsAllProducts 而不是 ProductsRepository。 Phil Calçado 有一个关于这个主题的 very good article,强烈推荐。

      How should this query be implemented?
      

      这个查询没有什么特别之处,它可以像产品存储库中的所有其他查询一样实现。查询本身对域是隐藏的,因为存储库实现属于数据访问层。数据访问可以实现任何查询,因为它对所有聚合及其关系有深入的了解。此时它只是一个 Hibernate 或 SQL 问题。

      【讨论】:

      • 我更喜欢 Szymon 的回答 - 在查询应该与域/repo 层分离这一点上非常直截了当。
      • 当答案无用时,您投反对票。我的没用吗?
      • BoughtByUser 需要一个User - 这意味着您必须花费宝贵的时间来获取一个User 实例只是为了使用它的ID 进行查询?为什么不直接传递userId
      • @Dmitry 文章不再可用,您是否碰巧有链接或缓存版本?
      • @Agramer 添加了指向 archive.org 的链接
      【解决方案3】:

      首先,很少针对聚合根进行查询。它们是针对数据完成的,只返回数据。存储库是在应用程序层(命令等)代码中使用的非常方便的持久性抽象。我们在那里需要它们,因为我们希望能够在不需要数据库的情况下测试这一层。这就是为什么存储库越小越好——模拟它更容易。

      我倾向于使用允许我的 UI 查询数据存储的专用 Finder 对象。我什至将我的查找器放在 UI 层中。问题是,每次 UI 更改时它们都会发生变化,因此最好将它们放在一起。您不想将查询方法放在存储库上的另一个很好的原因是,存储库是您的域的一部分,是您无处不在的语言。您不想让那些往往寿命短且变化迅速的 UI 概念污染它们。

      前段时间我写了一篇博文来解释这个概念。你可以找到它here

      【讨论】:

      • +1 - 一旦我意识到查询数据以获取 UI 和域模型实际上是两个独立的概念,我的生活(和我的设计)就变得更容易了。为什么要让事情复杂化 - 域模型应该针对业务域而不是 UI 要求进行定制。
      • 在此处存档博客文章链接:web.archive.org/web/20160403195814/http://…
      猜你喜欢
      • 2012-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-11
      • 2010-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多