是否可以绕过复杂查询的存储库模式?
这是我目前对 DDD 的理解:
- 严格的存储库模式应该只实现 get()、delete() 和 create(),也许还有 get() 的变体,可以在其中搜索或检索整个集合,
- 这很常见每个聚合根都有一个存储库
(根据研究,我知道这些不是普遍接受的规范)
这里的问题是如何实现涉及许多聚合根的复杂查询。例如,我们有两个聚合根 - 产品和用户。如果我正在创建一个列出用户购买了哪些产品的页面,那么我有一个跨越用户聚合和产品聚合的查询。
这个查询应该如何实现呢?
我现在所做的实际上是为该查询和具有相关功能的查询建立一个存储库(有些人会不同意并说存储库不是查询层)。
仅使用产品和用户的存储库,获取所有记录并在内存中执行所有操作(这听起来是错误的)
让查询(LINQ 或 SQL)位于服务内部,而不使用与聚合关联的存储库。
还有其他一些方法吗?
This is my understanding about DDD at the moment:
- The strict repository pattern should only implement get(), delete() and create(), and maybe variants of get() where one can search or to retrieve an entire collection
- It is common for each aggregate root to have one repository
(from research, I know those are not universally accept norms)
The question here is how to implement complex queries which involves many aggregate roots. For example, we have two aggregate roots - product and user. If I am doing a page which list what products a user have bought, then I have a query which stretch across both the user aggregate and the product aggregate.
How should this query be implemented?
What I am doing now is actually to have a repository for this query and queries with related functionality (some will disagree and say the repository is not a query layer).
Use only the repository for product and user, grab all records and do everything in memory (this sounds wrong)
Have the query (LINQ or SQL) to be inside the service, not using the repository associated with the aggregates at all.
Are there some other ways?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
存储库接口是您域的一部分,应尽可能基于通用语言。所有存储库都不同,就像您的所有聚合都不同一样。严格来说,通用存储库是 CRUD 过度泛化,可能会降低代码表现力。 “Create”方法也不属于存储库,因为对象生命周期的开始通常由工厂或对象本身处理。当您想要保留现有对象时,“添加”似乎是一个更好的名称,因为存储库具有集合语义。
在这种情况下,你只需要听取业务需求,我强调了我认为最重要的部分。基于此,您似乎需要:
像这样组织代码的想法是尽可能匹配业务需求和通用语言。存储库接口的命名也很重要,我更喜欢使用 Products 或 AllProducts 而不是 ProductsRepository。 Phil Calçado 有一篇关于此主题的非常好的文章 ,强烈推荐。
该查询没有什么特别之处,它可以像产品存储库中的所有其他查询一样实现。查询本身对域是隐藏的,因为存储库实现属于数据访问层。数据访问可以实现任何查询,因为它对所有聚合及其关系有深入的了解。此时这只是一个 Hibernate 或 SQL 问题。
Repository interface is part of your domain and should be based on Ubiquitous Language as much as possible. All Repositories are different just like all your Aggregates are different. Strict, generic repositories are CRUD overgeneralization and may reduce code expressiveness. 'Create' method also does not belong to Repository because beginning of object life cycle is usually handled by Factory or Object itself. 'Add' seems like a better name when you want to persist existing object because Repository has a collection semantics.
In this case you just have to listen to the business requirements, I emphasized the part that I think is most important. Based on that it looks like you need:
The idea of organizing code like that is to match business requirements and ubiquitous language as much as possible. The naming of the repository interfaces is also important, I prefer to have Products or AllProducts instead of ProductsRepository. Phil Calçado has a very good article on this subject, highly recommended.
There is nothing special about this query, it can be implemented just like all other queries in Products repository. The querying itself is hidden from Domain because Repository implementation belongs to Data Access layer. Data Access can implement any query because it has intimate knowledge of all the Aggregates and their relationships. At this point it would just be a Hibernate or SQL question.
首先,很少针对聚合根进行查询。它们是针对数据完成的并且仅返回数据。存储库是在应用程序层(命令等)代码中使用的非常方便的持久性抽象。我们需要它们,因为我们希望能够在不需要数据库的情况下测试这一层。这就是为什么存储库越小越好——更容易模拟它。
我倾向于使用专门的 Finder 对象来允许我的 UI 查询数据存储。我什至将 Finders 放置在 UI 层中。问题是,每次 UI 更改时它们都会发生变化,因此最好将它们放在一起。您不想将查询方法放在存储库上的另一个很好的原因是,存储库是您的领域、您的通用语言的一部分。您不想让那些往往短暂且快速变化的 UI 概念污染它们。
我前段时间写了一篇博客文章解释这个概念。您可以找到它 这里。
First of all, queries are rarely done against Aggregate Roots. They are done against data and return just data. Repositories are very handy abstractions of persistence for using in application layer (commands and such) code. We need them there because we want to be able to test this layer without need for a database. That's why the smaller the repository the better -- it's easier to mock it.
I tend to use specialized Finder objects that allow my UI to query the data store. I even place my Finders in the UI layer. The thing is, they tend to change every time UI changes so it's better to put them together. Another good reason why you don't want to put query methods on the repository is, repository is part of your domain, your ubiquitous language. You don't want to pollute them with UI concepts that tend to be short living and rapidly changing.
I've written a blog post explaining this concept some time ago. You can find it here.
只需将其放入存储库类中即可。它是一个 Get,可能在 ProductRepository 中,因为这就是它返回的内容。 GetProductsByUser(int UserID)。或者,如果您有一个 n 层架构,那么它可以位于服务方法中。
我一直认为存储库是放置任何数据库访问方法的地方,因为这个想法是将数据库代码与其他所有代码分开。
Just put it in a repository class. It is a Get, probably in the ProductRepository as that is what it is returning. GetProductsByUser(int UserID). Or if you have an n-Tier architecture, then it can be in the service method.
I have always thought of the repository as a place to put any database-accessing methods as the idea is the separate the database code from everything else.