具有多个实体的存储库创建DTO

Nul*_*sis 6 c# architecture domain-driven-design repository-pattern asp.net-web-api

在这里做一些C#代码审查,并注意到开发人员在5个附加表中进行连接,并在DTO中进行内联IQueryable投影.在存储库中加入的表是User,Appointment,CommunicationPreference,UserProductProduct.我没有设计系统,我可能在某种程度上简化了它,但是现在就让我们按照这个原则进行操作.使用Entity Framework和Web API.

这有点长,所以我道歉,但真的好奇听到别人对这件事的看法.

因此,这些表的相关之处在于每个用户都有一个即将到来的服务预约列表来为其产品提供服务.UserProduct表显示产品和用户之间的关系,product表显示产品的种类.CommunicationPreference处理他们希望如何联系他们的服务,例如他们的服务何时到期,是否有人来服务产品,他们希望如何联系(电话电子邮件文本等).

因此,开发人员将这些表连接在存储库本身,然后创建一个名为"UserServiceOverviewDto"的DTO,它返回到控制器然后返回到前端,并且基本上有信息以网格形式显示给客户代表(用户是谁) ,即将到来的约会,什么产品,基本上我刚才写的一切).

我理解为什么他们这样做 - 而不是对5个不同的存储库进行5次调用,而是将它们全部组合在一起,然后不必将每个存储器转换为DTO,而是一次性完成所有操作.但我知道存储库也不应该返回DTO.理想情况下,您的存储库应返回逻辑上聚合的内容,并代表解决您问题的一个实体.

在我考虑这个的时候,我偶然发现了这个在查询Repository层中的多个实体时返回什么类型?但它并没有真正帮助我,并希望就如何做更多的建议.

从DDD的角度来看,我想知道需要引入什么新的"东西".也许我需要创建一个新的上下文/存储库,这是一个新的东西.如果我试图说这是一个常见的分组,并给它一个像"X"的名称,这是否意味着我想创建一个新的数据库表来表示?可能不是,因为我已经有了存储这些信息的表格.

这是否意味着我想创建一个名为"X"的新类而没有相应的数据库表?那么这与DTO有何不同?实际上,它几乎暗示通过创建这个类,我试图通过实例化X而不是XDto来使我的存储库更纯粹,然后将X转换为服务层中的DTO(除了转移之外我还没有获得任何东西周围的东西,并创造一个非常贫穷的实体).

总而言之,问题是:

如果我在前端为了方便而将多个实体组合在一起,那么它是否更好:

  1. 让服务层进行5次不同的数据库调用,并在此层组装DTO以返回控制器?这不是坏事吗?5个电话只是为了让我的代码看起来更"纯粹"?一次性完成连接并从数据库中获取所需内容不是更好吗?
  2. 在C#中创建一个表示这5个表的结合的新类,然后在该类和相应的DTO之间实现映射.正如我上面所说,这个课程不会被坚持下去.
  3. 让存储库执行其连接以创建新的DTO作为内联投影(似乎错误的b/c存储库应该返回实体而不是DTO)

真的很期待这里的一些反馈!谢谢!

the*_*Dmi 6

这个问题不是特定于 CQRS 的,在它之外也有效,所以我会这样回答。

你描述的情况在实现统计功能的时候经常出现。例如,回答诸如“有多少用户订购了该产品,按天分组?”之类的问题。这有时称为用例优化查询

在这些情况下,您建议的第二种方法通常是最好的。创建一个新类型来对此类操作的结果进行建模。聚合存储库中的所有数据并返回该类型的实例。

解决方案#1经常是非常低效的,因为服务层是不能够使用聚合和分组功能的DB优惠。解决方案#3 违反了单一职责原则,迟早会导致可维护性问题。

新类型的设计注意事项

  • 返回的数据始终是只读的,因此可以将该类型建模为value object
  • 如果类型在域中有意义,则将其放入域层。如果没有,把它放在持久层。


小智 1

看看CQRS,它几乎是这个主题的精确讨论。基本上,您可能应该创建一个新对象来表示此聚合,但要知道这样做会给您的项目带来显着的复杂性。你创建的这个东西不能被更新/持久化,它只能被查询。更新与其关联的其他实体必须通过单独的域模型来完成。