DDD – 如何实现高性能的搜索存储库
我有关于DDD和存储库模式的问题。
假设我有Customer聚合根的Customer存储库。 Get&Find方法返回完全填充的聚合,其中包括Address等对象。一切都很好。 但是当用户在UI中搜索客户时,我只需要聚合的“摘要” – 只是一个包含汇总信息的扁平对象。
我可以解决这个问题的一种方法是正常调用存储库中的find方法,然后在应用程序层中将每个客户聚合映射到CustomerSearchResult / CustomerInfo DTO,并将它们发送回客户端。
但我的问题是性能; 每个Customer聚合可能需要多个查询来填充所有关联。 因此,如果我的搜索条件与50个客户相匹配,那么对于可能检索数据的数据库而言,这是我非常不需要的。
另一个问题是,我可能希望包含有关客户聚合根边界之外的客户的汇总数据,例如最后订单的日期。 订单有自己的聚合,因此要获取客户的订单信息,我必须调用OrderRepository,这也会降低性能。
所以现在我觉得我有两种选择:
-
向CustomerRepository添加一个额外的Find方法,该方法通过执行一个有效的查询来返回这些摘要对象的列表。
-
创建一个专门构建的只读CustomerInfoRepository,它只有1中描述的find方法。
但这两种感觉都让我觉得我违背了DDD的原则。 我的存储库inheritance自通用基础:存储库,其中T:IAggregateRoot。 这些摘要信息对象不是聚合,并且与T的类型不同,所以#1真的违背了设计。
也许对于#2,我会创建一个没有IAggregateRoot约束的抽象SearchRepository?
我的域名中有许多类似的场景。
你会如何实现这种情况?
谢谢,戴夫
更新
在阅读Theo的答案之后,我想我会选择#2选项并在我的基础架构中创建一个专门针对这些场景的SearchRepository。 然后,应用程序层(WCF服务)可以调用这些直接填充摘要DTO的存储库,而不是将域实体映射到DTO。
****更新2 ****
虽然我在一年前问过这个问题,但我想我只是补充一点,因为我发现了CQRS,旨在解决这个问题。 Udi Dahan( http://www.udidahan.com/ )和Greg Young( http://codebetter.com/gregyoung/ )已经写了很多关于它的文章。 如果您使用DDD创建分布式应用程序,CQRS适合您!
我认为你只想显示汇总信息。 这些汇总信息位不是域模型的实体或值对象。 它们只是信息,仅此而已。
这类似于显示报告信息。 如果我处理这些事情,我就不会坚持使用纯DDD方法。 您建议的选项没问题,因为它正在完成您的工作。 DDD不应被视为教条。 创造性思考。 放松一点DDD。
但请注意,您只是在模型外部创建信息值以显示目的。 因此,如果用户选择一位信息来对其进行某些操作(在域模型中定义),则需要从信息值中提取标识符,并从存储库中提取实体/值对象/聚合。
我强烈推荐这个video: Eric Evans:自从这本书以来我对DDD的了解 。 如果你读了他的书,你真的应该看到整个video。 在大约30:00的时间密切关注埃里克埃文斯自己谈论聚合,并提到你目前的问题。
我会:
- 返回表示我的对象视图的不同对象,例如CustomerInfo。
- 返回一个DataTable。 通用容器通常是最简单和最好的方式。
如果您的通用基础存储库中的T是客户,那么我认为您错误地应用了聚合根的概念,尽管我不是严格的Evansangelist 。 我将为Customer设计一个存储库,该存储库返回与客户逻辑或轻松组合的任何数据,包括DataTables或作为Customer数据视图的只读对象。