DDD:存储库合同

时间:2011-09-05 01:04:33

标签: orm domain-driven-design repository ddd-repositories design-by-contract

我在不同的地方读到DDD的一个重要要求是要有一个有限的存储库合同:

findByName(string name)
findByEmail(string email)
etc.

并没有提供通用查询界面:

findBySpecification(Specification spec)

我确实理解为什么这很重要:能够模拟存储库以进行测试,或者更改底层持久性框架。

虽然这条规则在整个应用程序中并不难实施,但在向用户提供“高级搜索”表单时,我无法弄清楚如何强制执行它。

假设我有一个表单,允许通过关键字日期作者等搜索博客帖子。

这些标准可以自由组合,我显然不能为每个用例提供一种方法:

findByKeyword(string keyword)
findByDateRange(Date from, Date to)
findByKeywordAndDateRange(string keyword, Date from, Date to)
findByDateRangeAndAuthor(Date from, Date to, User author)
etc.

我错过了什么或者这是规则的例外之一吗?

3 个答案:

答案 0 :(得分:2)

Specification作为参数传递给存储库没有任何问题。这实际上是解决存储库接口上方法爆炸的一种非常好的方法。看一下这个answer。对于“高级搜索”方案,“过滤器”可能比“规范”更合适。我认为此代码不会违反任何DDD指南:

Filter filter = new FilterBuilder()
    .WithinDateRange(dateRange)
    .IncludingKeywords("politics", "news")
    .ByAuthor("John Smith")
    .Build();

blogs.FindByFilter(filter);

请注意,创建过滤器的代码可以在域外生效。因为它不会违反任何域规则。如果有像“由匿名作者发布的博客应该由主持人批准”这样的规则怎么办?尽管可以用Filter表示,但这样做会外化业务逻辑。将此规则放入域代码并使用专用存储库方法更有意义,如:

blogs.RequireModeratorAttention();

答案 1 :(得分:1)

存储库模式有两个主要好处:

  1. 它将您的应用程序与持久层和成语分离。
  2. 它集中并限制对明确定义和理解的域段的数据访问(存储库中的数据访问方法具有明确定义的结果并且可以合理地测试)。
  3. 如果您使用持久层提供的规范模式的实例(例如NHibernate Criteria),则可以否定一个实例。完全使用规范模式(即使是你自己推出的模式)也可以降低第二个优势。

    话虽如此,在某些情况下,例如搜索界面,规范是必要的 - 只需确保并推出自己的规范。

答案 2 :(得分:1)

  

虽然这个规则在整个过程中并不难实施   应用程序,我无法弄清楚如何执行它   为用户提供“高级搜索”表单。

实际上,如果您需要的只是搜索表单,则无需支付所有这些抽象的费用。存储库(至少在DDD的上下文中)旨在从业务逻辑(应用程序层)中抽象出持久性框架的细微差别。

如果你有一个更改用户地址的命令,最好有一个使用FindUserById方法的存储库,而不是在app层中有一些神奇的Hibernate代码。这有两个原因

  • 您可能希望使用持久层测试应用程序层 (存储库)嘲笑
  • 应用程序层是您关心的事情 因为这是商业逻辑

您不需要所有这些来获取UI的一些数据。我建议使用甚至可能存在于UI层的专门的“Finder”类。它们既可以在抽象的'规范'上运行,也可以在裸Hibernate(或你最喜欢的ORM)上运行(甚至更好)。我写了一篇关于这种方法的博客文章here