DDD:存储库合同

5

我在多个地方看到过一个DDD中的重要需求,即Repository需要有一个有界约定:

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个回答

2
规格作为参数传递给仓储是没有问题的。这实际上是处理仓储接口中方法爆炸的一种很好的方式。请看这个答案,'Filter'可能比'规格'更适合“高级搜索”场景的名称。我认为这段代码不会违反任何DDD准则:
Filter filter = new FilterBuilder()
    .WithinDateRange(dateRange)
    .IncludingKeywords("politics", "news")
    .ByAuthor("John Smith")
    .Build();

blogs.FindByFilter(filter);

请注意,创建过滤器的代码可以存在于域外部,因为它不会违反任何域规则。如果有一条规则,例如“匿名作者发布的博客应由管理员批准”,虽然可以通过过滤器来表达,但这样做将使某些业务逻辑外部化。更明智的做法是将此规则放入域代码中,并有一个专门的存储库方法,例如:
blogs.RequireModeratorAttention();

1

仓储模式有两个主要优点:

  1. 它将您的应用程序与持久层和语言解耦。
  2. 它将数据访问集中和限制在定义明确且易于理解的领域段中(存储库中的数据访问方法具有明确定义的结果并且可以进行合理的测试)。

如果您使用持久层提供的规范模式实例(例如 NHibernate Criteria),则会抵消第一个优点。使用规范模式(即使是自己编写的规范模式)都会削弱第二个优点。

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


1
虽然在整个应用程序中执行此规则并不难,但我无法想出如何在为用户提供“高级搜索”表单时执行此规则。
实际上,如果您只需要一个搜索表单,那么您不必支付所有这些抽象的成本。存储库(至少在DDD的上下文中)旨在将持久性框架的细微差别与业务逻辑(应用程序层)分离。
如果您有一个更改用户地址的命令,则最好拥有具有FindUserById方法的存储库,而不是在应用程序层中使用一些神奇的Hibernate代码。原因有两个:
1. 您可能希望使用模拟的持久性层(存储库)测试应用程序层 2. 应用程序层是您关心的内容,因为它是业务逻辑
您不需要所有这些内容来获取UI的一些数据。我建议使用专门的“Finder”类,它们甚至可以存在于UI层中。它们可以在抽象的“规范”上或者更好地在裸露的Hibernate(或您喜欢的ORM)上运行。我写了一篇关于这种方法的博客文章here

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接