服务应该总是返回DTO,还是可以返回域模型?

我正在(重新)devise大型应用程序,我们使用基于DDD的多层体系结构。

我们有数据层(存储库的实现),领域层(领域模型和接口 – 存储库,服务,工作单元的定义),服务层(服务的实现)的MVC。 到目前为止,我们使用跨所有图层的领域模型(主要是实体),我们只使用DTO作为视图模型(在控制器中,服务返回域模型和控制器创build视图模型,并将其传递给视图)。

我读过无数关于使用,不使用映射和通过DTO的文章。 我知道没有任何明确的答案,但我不确定是否可以或不从服务返回到控制器的域模型。 如果我返回域模型,它仍然不会传递给视图,因为控制器总是创build视图特定的视图模型 – 在这种情况下,它看起来是合法的。 另一方面,域模型离开业务层(服务层)时,感觉不对。 有时候服务需要返回没有在域中定义的数据对象,然后我们必须添加新的对象到未映射的域,或者创buildPOCO对象(这很丑陋,因为有些服务返回域模型,有些有效地返回DTO)。

问题是,如果我们严格使用视图模型,将域模型一直返回给控制器是否可行,还是应该总是使用DTO与服务层进行通信? 如果是这样,可以根据需要调整域模型吗? (坦率地说,我不这么认为,因为服务应该消费域名。)如果我们应该严格遵守DTO,是否应该在服务层定义? (我是这样认为的)有时很明显我们应该使用DTO(例如,当服务执行大量的业务逻辑并创build新的对象时),有时很明显我们应该只使用域模型(例如,当会员服务返回贫血的用户s) – 创build与领域模型相同的DTO似乎没有什么意义) – 但我更喜欢一致性和良好的实践。

文章域与DTO vs ViewModel – 如何以及何时使用它们? (还有其他一些文章)与我的问题非常相似,但是它不能回答这个问题。 文章我应该用EF来实现仓库模式的DTO吗? 也是类似的,但是它不处理DDD。

免责声明:我不打算使用任何devise模式,只是因为它存在而且很花哨,另一方面,我想使用好的devise模式和实践,因为它有助于devise整个应用程序,帮助分离至less目前来说,即使使用特定的模式也不是“必要的”。

一如既往,谢谢。

当域模型离开业务层(服务层)时,感觉不对,

让你觉得你正在把胆量拉出来吗? 根据Martin Fowler的说法:服务层定义了应用程序的boundery,它封装了域。 换句话说,它保护了域名。

有时服务需要返回域中未定义的数据对象

你能提供一个这个数据对象的例子吗?

如果我们应该严格遵守DTOs,是否应该在服务层定义?

是的,因为响应是你的服务层的一部分。 如果将其定义为“别的地方”,则服务层需要引用“其他地方”,为您的烤宽面条添加新的图层。

是否可以将域模型一直返回给控制器,还是我们应该始终使用DTO与服务层进行通信?

一个DTO是一个响应/请求对象,如果你使用它进行通信是有意义的。 如果在表示层(MVC-Controllers / View,WebForms,ConsoleApp)中使用域模型,则表示层紧密耦合到您的域,域中的任何更改都需要您更改控制器。

似乎创build与领域模型相同的DTO没有多大意义)

这是DTO对新眼睛的缺点之一。 目前,您正在考虑重复代码 ,但是随着项目的扩展,特别是在团队环境中,不同的团队被分配到不同的层次,这样做会更有意义。

DTO可能会增加您的应用程序的复杂性,但是您的图层也是如此。 DTO是您的系统的一个昂贵的function,他们不来免费。

为什么要使用DTO

本文提供了使用DTO的优点和缺点, http ://guntherpopp.blogspot.com/2010/09/to-dto-or-not-to-dto.html

总结如下:

何时使用

  • 对于大型项目。
  • 项目寿命10年及以上。
  • 战略,关键任务应用程序。
  • 大型团队(超过5人)
  • 开发人员在地理上分布。
  • 域和演示文稿是不同的。
  • 减less开销数据交换(DTO的最初目的)

何时不使用

  • 中小型项目(最多5人)
  • 项目寿命是2年左右。
  • GUI,后端等没有单独的团队

对DTO的争论

与DTO的争论

  • 没有DTO,演示文稿和领域紧密结合。 (这对于小型项目是可以的)
  • 接口/ API稳定
  • 可以通过返回仅包含那些绝对需要的属性的DTO来为表示层提供优化。 使用linq投影 ,你不必拉整个实体。
  • 为了降低开发成本,请使用代码生成工具

根据我的经验,你应该做一些实际的事情。 “最好的devise是最简单的devise” – 爱因斯坦。 这就是…

如果我们严格使用视图模型,将域模型一直返回给控制器是否可行,还是应该总是使用DTO与服务层进行通信?

绝对没关系! 如果你有域实体,DTO的和查看模型,然后包括数据库表,你有应用程序中的所有领域在4个地方重复。 我曾在大型项目的领域实体和视图模型工作得很好。 唯一的解释是如果应用程序是分布式的,服务层驻留在另一台服务器上,在这种情况下,由于序列化原因DTO需要通过线路发送。

如果是这样,可以根据需要调整域模型吗? (坦率地说,我不这么认为,因为服务应该消费域名。)

一般来说,我会同意并且说不,因为域模型通常是业务逻辑的反映,并且通常不会被该逻辑的消费者所塑造。

如果我们应该严格遵守DTOs,是否应该在服务层定义? (我想是这样。)

如果你决定使用它们,我会同意,并说服务层是一个完美的地方,因为它是在一天结束的时候返回DTO的。

祝你好运!

看起来您的应用程序非常庞大复杂,因为您已经决定采用DDD方法。 不要将您的poco实体或所谓的域实体和值对象返回给您的服务层。 如果你想这样做,那么删除你的服务层,因为你不再需要它了! 视图模型或数据传输对象应该位于服务层,因为它们应该映射到域模型成员,反之亦然。 那么为什么你需要有DTO? 在复杂的应用场景中,您应该将领域和演示视图的关注区分开来,领域模型可以分成多个DTO,还可以将多个领域模型合并为一个DTO。 所以最好在分层架构中创buildDTO,即使它和你的模型一样。

我们应该总是使用DTO与服务层进行通信吗? 是的,您必须通过服务层返回DTO,因为您已经使用域模型成员与服务层中的存储库交谈,并将它们映射到DTO并返回到MVC控制器,反之亦然。

可以根据需要调整域模型吗? 一个服务只是与存储库和域的方法和域服务对话,您应该根据您的需求解决您的域中的业务,而告诉域需要什么不是服务任务。

如果我们应该严格遵守DTOs,是否应该在服务层定义? 是的,尝试让DTO或ViewModel稍后才能使用,因为它们应该映射到服务层的域成员,并且将DTO放在应用程序的控制器中(尝试在服务层中使用请求响应模式)并不是一个好主意,干杯!

到目前为止,我们在所有图层中使用域模型(主要是实体),并且我们仅将DTO用作视图模型(在控制器中,服务返回域模型和控制器创build视图模型,并将其传递给视图)。

由于域模型为整个应用程序提供了术语( 无处不在的语言 ),所以最好使用广泛的域模型。

使用ViewModels / DTOs的唯一原因是在应用程序中实现MVC模式以分离View (任何types的表示层)和Model (域模型)。 在这种情况下,您的演示文稿和领域模型是松散耦合的。

有时服务需要返回域中没有定义的数据对象,然后我们要么添加新的对象到未映射的域,要么创buildPOCO对象(这很丑陋,因为有些服务返回域模型,有些有效地返回DTO)。

我假设你谈论应用程序/业务/域逻辑服务。

我build议你尽可能地返回域实体。 如果需要返回附加信息,返回包含多个域实体的DTO是可以接受的。

有时候,使用第三部分框架的人(通过域实体生成代理)面临着将域实体从服务中暴露出来的困难,但这只是错误用法的问题。

问题是,如果我们严格使用视图模型,将域模型一直返回给控制器是否可行,还是应该总是使用DTO与服务层进行通信?

我认为这足以在99.9%的情况下返回域实体。

为了简化DTO的创build并将你的域实体映射到它们中,你可以使用AutoMapper 。

我build议分析这两个问题:

  1. 您的上层(即视图和视图模型/控制器)是否以不同的方式使用数据,而这些数据是域图层公开的? 如果有很多映射正在进行,甚至涉及到逻辑,我会build议重新审视你的devise:它应该更接近实际使用数据的方式。

  2. 你怎么可能深深地改变你的上层? (例如交换WPF的ASP.NET)。 如果这是非常不同的,你的架构不是很复杂,你可能会更好地暴露尽可能多的领域实体,你可以。

恐怕这是一个相当广泛的话题,它真的让你的系统和它的要求变得复杂。

我迟到了这个晚会,但这是一个很普遍,很重要的问题,我觉得不得不回应。

“服务”是指埃文在蓝皮书中描述的“应用层”吗? 我会假设你做的,在这种情况下,答案是他们不应该返回DTO。 我build议阅读题为“隔离域名”的蓝皮书第4章。

在那一章,埃文斯说了以下关于层:

将一个复杂的程序分割成图层。 在每个层次内开发一个具有凝聚力的devise,并且仅依赖于下面的层。

这是有充分的理由。 如果您使用偏序的概念作为软件复杂度的度量,那么让一个图层依赖于它上面的图层会增加复杂性,从而降低可维护性。

把这个应用到你的问题上,DTO实际上是一个适配器,是用户界面/表示层的关注点。 请记住,远程/跨进程通信正是DTO的目的 (值得注意的是,在那个时候,Fowler也反对DTO作为服务层的一部分,虽然他不一定是说DDD语言)。

如果您的应用程序层依赖于这些DTO,则取决于其上面的层,并且复杂性会增加。 我可以保证这会增加维护您的软件的难度。

例如,如果您的系统与其他几个系统或客户端types接口,每个系统都需要自己的DTO? 你怎么知道你的应用程序服务应该返回哪个DTO? 如果你select的语言不允许重载一个基于返回types的方法(在这种情况下是服务方法),你甚至可以解决这个问题? 即使你找出一个办法,为什么违反你的应用层来支持表示层的关注?

实际上,这是以意大利式面条build筑结束的道路。 我以自己的经验看到了这种权力下放和结果。

在我目前工作的地方,我们的应用层中的服务返回域对象。 我们不认为这是一个问题,因为接口(即UI /表示层)取决于它下面的域层。 而且,这种依赖被最小化为“仅供参考”types的依赖,因为:

a)接口层只能将这些域对象作为通过调用应用层获得的只读返回值来访问

b)应用层中的服务方法只接受该层中定义的“原始”input(数据值)或对象参数(以在必要时减less参数计数)作为input。 具体来说,应用程序服务从不接受域对象作为input

接口层使用在接口层本身内定义的映射技术来从域对象映射到DTO。 同样,这使得DTO专注于由接口层控制的适配器。