我在不同的地方读到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.
我错过了什么或者这是规则的例外之一吗?
答案 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)
存储库模式有两个主要好处:
如果您使用持久层提供的规范模式的实例(例如NHibernate Criteria),则可以否定一个实例。完全使用规范模式(即使是你自己推出的模式)也可以降低第二个优势。
话虽如此,在某些情况下,例如搜索界面,规范是必要的 - 只需确保并推出自己的规范。
答案 2 :(得分:1)
虽然这个规则在整个过程中并不难实施 应用程序,我无法弄清楚如何执行它 为用户提供“高级搜索”表单。
实际上,如果您需要的只是搜索表单,则无需支付所有这些抽象的费用。存储库(至少在DDD的上下文中)旨在从业务逻辑(应用程序层)中抽象出持久性框架的细微差别。
如果你有一个更改用户地址的命令,最好有一个使用FindUserById方法的存储库,而不是在app层中有一些神奇的Hibernate代码。这有两个原因
您不需要所有这些来获取UI的一些数据。我建议使用甚至可能存在于UI层的专门的“Finder”类。它们既可以在抽象的'规范'上运行,也可以在裸Hibernate(或你最喜欢的ORM)上运行(甚至更好)。我写了一篇关于这种方法的博客文章here。