是否可以绕过复杂查询的存储库模式?

Ext*_*kun 18 design-patterns domain-driven-design repository

这是我对DDD的理解:

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

(从研究中,我知道那些不是普遍接受的规范)

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

该查询应该如何实现?

  1. 我现在正在做的事实上是拥有这个查询的存储库和具有相关功能的查询(有些人会不同意并说存储库不是查询层).

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

  3. 让查询(LINQ或SQL)在服务内部,而不是使用与聚合相关联的存储库.

还有其他方法吗?

Dmi*_*try 16

严格的存储库模式应该只实现get(),delete()和create(),以及get()的变体,其中可以搜索或检索整个集合

存储库接口是您域的一部分,应尽可能基于泛在语言.所有存储库都是不同的,就像所有聚合都不同一样.严格的通用存储库是CRUD过度概括,可能会降低代码表达能力.'Create'方法也不属于Repository,因为对象生命周期的开始通常由Factory或Object本身处理.当你想要持久化现有对象时,'添加'似乎是一个更好的名称,因为存储库具有集合语义.

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

在这种情况下,您只需要听取业务要求,我强调了我认为最重要的部分.基于它看起来你需要:

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

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

How should this query be implemented?
Run Code Online (Sandbox Code Playgroud)

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

  • 当答案无效时,你要投票.我的没用吗? (9认同)

Szy*_*ega 14

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

我倾向于使用专门的Finder对象,允许我的UI查询数据存储.我甚至将我的Finders放在UI层中.问题是,每次UI更改时它们都会发生变化,因此最好将它们组合在一起.您不希望在存储库中放置查询方法的另一个好理由是,存储库是您的域的一部分,您的无处不在的语言.您不希望使用易于生存和快速变化的UI概念来污染它们.

我前段时间写了一篇博客文章解释这个概念.你可以在这里找到它.

  • +1 - 一旦我意识到查询 UI 和域模型的数据实际上是两个独立的概念,我的生活(和我的设计)就变得更容易了。为什么使事情复杂化 - 域模型应该针对业务领域而不是 UI 要求进行定制。 (3认同)