DDD:存储库合约

发布于 2024-12-02 19:31:24 字数 689 浏览 1 评论 0原文

我在很多地方读到,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.

我是否遗漏了某些内容,或者它是规则的例外情况之一?

I've read in various places that one important requirement in DDD is to have a bounded contract for the Repository:

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

And not provide a generic query interface:

findBySpecification(Specification spec)

I do understand why this is important: to be able to mock the Repository for tests, or change the underlying persistence framework.

While this rule is not that hard to enforce throughout the application, I can't figure out how to enforce it when it comes to provide the user with an "advanced search" form.

Let's say I have a form which allows to search blog posts by keyword, by date, by author, etc.

These criteria being freely combinable, I obviously can't provide a method for each use case:

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

Am I missing something or is it one of the exceptions to the rule?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

半暖夏伤 2024-12-09 19:31:24

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

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

blogs.FindByFilter(filter);

请注意,创建过滤器的代码可以位于域之外。因为它不会违反任何域规则。如果存在诸如“匿名作者发布的博客应得到版主批准”之类的规则怎么办?虽然可以用Filter来表达,但这样做会将业务逻辑具体化。将此规则放入域代码中并拥有专用的存储库方法会更有意义,例如:

blogs.RequireModeratorAttention();

There is nothing wrong with passing Specification as a parameter to a repository. This is actually a very good way of tackling method explosion on the repository interface. Take a look at this answer. 'Filter' maybe more appropriate name than 'Specification' for the 'advanced search' scenario. I think this code will not violate any DDD guidelines:

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

blogs.FindByFilter(filter);

Note that the code that creates the filter can live outside of domain. Because it will not violate any domain rules. What if there is a rule like "Blogs posted by Anonymous author should be approved by moderator"? Although it can be expressed with Filter, doing so will externalize piece of business logic. It will make more sense to put this rule into domain code and have a dedicated repository method like:

blogs.RequireModeratorAttention();
百善笑为先 2024-12-09 19:31:24

虽然这条规则在整个国家中执行起来并不难
应用程序,我不知道如何强制执行它
为用户提供“高级搜索”表单。

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

如果您有一个更改用户地址的命令,那么最好有一个带有 FindUserById 方法的存储库,而不是在应用程序层中拥有一些神奇的 Hibernate 代码。有两个原因

  • 您可能想用持久层来测试应用程序层
    (repositories)mocked
  • 应用层是你关心的
    因为它是业务逻辑

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

While this rule is not that hard to enforce throughout the
application, I can't figure out how to enforce it when it comes to
provide the user with an "advanced search" form.

Actually, you don't have to pay the cost of all these abstractions if all you need is a search form. Repository (in context of DDD at least) was designed to abstract the nuances of persistence framework from the business logic (application layer).

If you have a command that changes user's address, it's better to have a repository with FindUserById method than to have some magic Hibernate code in the app layer. There are two reasons for this

  • You may want to test the application layer with persistence layer
    (repositories) mocked
  • Application layer is something you care about
    because it's business logic

You don't need all of these to just get some data for the UI. I would suggest using specialized 'Finder' classes that may even live in the UI layer. They can operate either on abstract 'specification' or (even better) on bare Hibernate (or your favourite ORM). I've written a blog post about this approach here.

相思碎 2024-12-09 19:31:24

存储库模式有两个主要优点:

  1. 它将应用程序与持久层和惯用法分离。
  2. 它将数据访问集中并限制在明确定义和理解的域段(存储库中的数据访问方法具有明确定义的结果并且可合理测试)。

如果您使用持久层提供的规范模式的实例(例如 NHibernate Criteria),您就否定了好处之一。完全使用规范模式(即使是您自己推出的规范模式)会削弱第二点的好处。

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

The Repository pattern has two main benefits:

  1. It decouples your application from your persistence layer and idiom.
  2. It centralizes and constrains data access to well-defined and understood domain segments (the data access methods in the repository have a well defined result and are reasonably testable).

If you use an instance of the specification pattern provided by your persistence layer (e.g. NHibernate Criteria), you negate benefit one. Using the specification pattern at all (even one you roll yourself) waters down the benefit of number two.

That being said, in some situations such as search interfaces, specification is necessary - just make sure and roll your own.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文