asp.net-mvc 服务应该总是返回 DTO,还是也可以返回域模型?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21554977/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 03:31:00  来源:igfitidea点击:

Should services always return DTOs, or can they also return domain models?

asp.net-mvcentity-frameworkarchitecturedomain-driven-design

提问by Robert Goldwein

I'm (re)designing large-scale application, we use multi-layer architecture based on DDD.

我正在(重新)设计大型应用程序,我们使用基于 DDD 的多层架构。

We have MVC with data layer (implementation of repositories), domain layer (definition of domain model and interfaces - repositories, services, unit of work), service layer (implementation of services). So far, we use domain models (mostly entities) accross all layers, and we use DTOs only as view models (in controller, service returns domain model(s) and controller creates view model, which is passed to the view).

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

I'v read countless articles about using, not using, mapping and passing DTOs. I understand that there's no any definitive answer, but I'm not sure if it's ok or not returning domain models from services to controllers. If I return domain model, it's still never passed to the view, since controller always creates view-specific view model - in this case, it seem legit. On the other hand, it doesn't feel right when domain model leaves business layer (service layer). Sometimes service needs to return data object that wasn't defined in the domain and then we either have to add new object to the domain that isn't mapped, or create POCO object (this is ugly, since some services return domain models, some effectively return DTOs).

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

The question is - if we strictly use view models, is it ok to return domain models all the way to controllers, or should we always use DTOs for communication with service layer? If so, is it ok to adjust domain models based on what services need? (Frankly I don't think so, since services should consume what domain has.) If we should strictly stick to DTOs, should they be defined in service layer? (I think so.) Sometimes it's clear that we should use DTOs (e.g., when service performs lot of business logic and creates new objects), sometimes it's clear that we should use just domain models (e.g., when Membership service returns anemic User(s) - it seems it wouldn't make much sense to create DTO that is the same as domain model) - but I prefer consistency and good practices.

问题是 - 如果我们严格使用视图模型,是否可以一直将域模型返回给控制器,还是应该始终使用 DTO 与服务层通信?如果是这样,是否可以根据服务需要调整域模型?(坦白说我不这么认为,因为服务应该消耗域所拥有的。)如果我们应该严格遵守 DTO,它们是否应该在服务层中定义?(我认为如此。)有时很明显我们应该使用 DTO(例如,当服务执行大量业务逻辑并创建新对象时),有时很明显我们应该只使用域模型(例如,当 Membership 服务返回贫血 User( s) - 创建与域模型相同的 DTO 似乎没有多大意义) - 但我更喜欢一致性和良好的实践。

Article Domain vs DTO vs ViewModel - How and When to use them?(and also some other articles) is very similar to my problem, but it doesn't answer this question(s). Article Should I implement DTOs in repository pattern with EF?is also similar, but it doesn't deal with DDD.

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

Disclaimer: I don't intend to use any design pattern only because it exists and is fancy, on the other hand, I'd like to use good design patterns and practices also because it helps designing the application as a whole, helps with separation of concerns, even tohugh using particular pattern isn't "necessary", at least at the moment.

免责声明:我不打算使用任何设计模式仅仅因为它存在并且很花哨,另一方面,我想使用好的设计模式和实践也是因为它有助于将应用程序设计为一个整体,有助于分离担心,即使使用特定模式也不是“必要的”,至少目前是这样。

As always, thank you.

一如既往,谢谢你。

回答by Yorro

it doesn't feel right when domain model leaves business layer (service layer)

领域模型离开业务层(服务层)感觉不对

Makes you feel like you are pulling the guts out right? According to Martin Fowler: the Service Layer defines the application's boundery, it encapsulates the domain. In other words it protects the domain.

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

Sometimes service needs to return data object that wasn't defined in the domain

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

Can you provide an example of this data object?

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

If we should strictly stick to DTOs, should they be defined in service layer?

如果要严格遵守DTO,是否应该在服务层定义?

Yes, because the response is part of your service layer. If it is defined "somewhere else" then the service layer needs to reference that "somewhere else", adding a new layer to your lasagna.

是的,因为响应是您服务层的一部分。如果它被定义为“其他地方”,那么服务层需要引用“其他地方”,为您的千层面添加一个新层。

is it ok to return domain models all the way to controllers, or should we always use DTOs for communication with service layer?

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

A DTO is a response/request object, it makes sense if you use it for communication. If you use domain models in your presentation layer (MVC-Controllers/View, WebForms, ConsoleApp), then the presentation layer is tightly coupled to your domain, any changes in the domain requires you to change your controllers.

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

it seems it wouldn't make much sense to create DTO that is the same as domain model)

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

This is one of the disadvantage of DTO to new eyes. Right now, you are thinking duplication of code, but as your project expands then it would make much more sense, specially in a team environment where different teams are assigned to different layers.

这是 DTO 对新人眼中的劣势之一。现在,您正在考虑重复代码,但是随着您的项目的扩展,它会变得更有意义,特别是在不同团队被分配到不同层的团队环境中。

DTO might add additional complexity to your application, but so are your layers. DTO is an expensive feature of your system, they don't come free.

DTO 可能会给您的应用程序增加额外的复杂性,但您的层也是如此。DTO 是您系统的一项昂贵功能,它们不是免费的。

Why use a DTO

为什么要使用 DTO

This article provides both advantage and disadvantage of using a DTO, http://guntherpopp.blogspot.com/2010/09/to-dto-or-not-to-dto.html

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

Summary as follows:

总结如下:

When to Use

何时使用

  • For large projects.
  • Project lifetime is 10 years and above.
  • Strategic, mission critical application.
  • Large teams (more than 5)
  • Developers are distributed geographically.
  • The domain and presentation are different.
  • Reduce overhead data exchanges (the original purpose of DTO)
  • 对于大型项目。
  • 项目寿命为10年及以上。
  • 战略性的关键任务应用程序。
  • 大型团队(5人以上)
  • 开发人员在地理上分布。
  • 领域和表现形式是不同的。
  • 减少开销数据交换(DTO 的初衷)

When not to Use

何时不使用

  • Small to mid size project (5 members max)
  • Project lifetime is 2 years or so.
  • No separate team for GUI, backend, etc.
  • 中小型项目(最多 5 名成员)
  • 项目寿命为2年左右。
  • GUI、后端等没有单独的团队。

Arguments Against DTO

反对 DTO 的论点

Arguments With DTO

与 DTO 的争论

  • Without DTO, the presentation and the domain is tightly coupled. (This is ok for small projects.)
  • Interface/API stability
  • May provide optimization for the presentation layer by returning a DTO containing only those attributes that are absolutely required. Using linq-projection, you don't have to pull an entire entity.
  • To reduce development cost, use code-generating tools
  • 没有 DTO,表示和域是紧密耦合的。(这对于小项目来说是可以的。)
  • 接口/API稳定性
  • 可以通过返回一个只包含那些绝对需要的属性的 DTO 来为表示层提供优化。使用linq-projection,您不必拉出整个实体。
  • 为了降低开发成本,使用代码生成工具

回答by Ehsan

It seems that your application is big and complex enough as you have decided to go through DDD approach. Don't return your poco entities or so called domain entities and value objects in you service layer. If you want to do this then delete your service layer because you don't need it anymore! View Model or Data transfer objects should live in Service layer because they should map to domain model members and vice versa. So why do you need to have DTO? In complex application with lots of scenarios you should separate the concerns of domain and you presentation views, a domain model could be divided into several DTO and also several Domain models could be collapsed into a DTO. So it's better to create your DTO in layered architecture even it would be the same as your model.

您的应用程序似乎足够大且复杂,因为您已决定采用 DDD 方法。不要在您的服务层中返回您的 poco 实体或所谓的域实体和值对象。如果您想这样做,请删除您的服务层,因为您不再需要它了!视图模型或数据传输对象应该存在于服务层中,因为它们应该映射到域模型成员,反之亦然。那么为什么需要DTO呢?在具有大量场景的复杂应用程序中,您应该将领域和表示视图的关注点分开,一个领域模型可以分为多个 DTO,也可以将多个领域模型折叠成一个 DTO。因此,最好在分层架构中创建 DTO,即使它与您的模型相同。

Should we always use DTOs for communication with service layer?Yes, you have to return DTO by your service layer as you have talk to your repository in service layer with domain model members and map them to DTO and return to the MVC controller and vice versa.

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

Is it ok to adjust domain models based on what services need?A service just talks to repository and domain methods and domain services, you should solve the business in your domain based on your needs and it's not the service task to tell the domain what is needed.

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

If we should strictly stick to DTOs, should they be defined in service layer?Yes try to have DTO or ViewModel just in service later because they should be mapped to domain members in service layer and it's not a good idea to places DTO in controllers of your application(try to use Request Responsepattern in your Service layer), cheers!

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

回答by Justin Ricketts

In my experience you should do what's practical. "The best design is the simplest design that works" - Einstein. With that is mind...

根据我的经验,您应该做实际的事情。“最好的设计是最简单有效的设计”——爱因斯坦。有了这个心...

if we strictly use view models, is it ok to return domain models all the way to controllers, or should we always use DTOs for communication with service layer?

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

Absolutely it's ok! If you have Domain Entities, DTO's and View Models then including database tables you have all the fields in the application repeated in 4 places. I've worked on large projects where Domain Entities and View Models worked just fine. The only expception to this is if the application is distributed and the service layer resides on another server in which case DTOs are required to send across the wire for serialization reasons.

绝对没问题!如果您有域实体、DTO 和视图模型,那么包括数据库表,您的应用程序中的所有字段都会在 4 个地方重复。我曾参与过领域实体和视图模型运行良好的大型项目。唯一的例外是如果应用程序是分布式的并且服务层驻留在另一台服务器上,在这种情况下,出于序列化的原因,需要 DTO 通过网络发送。

If so, is it ok to adjust domain models based on what services need? (Frankly I don't think so, since services should consume what domain has.)

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

Generally I'd agree and say no because the Domain model is typically a reflection of the business logic and doesn't usually get shaped by the consumer of that logic.

一般来说,我会同意并说不,因为域模型通常是业务逻辑的反映,通常不会由该逻辑的使用者塑造。

If we should strictly stick to DTOs, should they be defined in service layer? (I think so.)

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

If you decide to use them I'd agree and say yes the Service layer is the perfect place as it's returning the DTOs at the end of the day.

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

Good luck!

祝你好运!

回答by BitMask777

I'm late to this party, but this is such a common, and important, question that I felt compelled to respond.

我参加这个聚会迟到了,但这是一个如此常见且重要的问题,我觉得有必要做出回应。

By "services" do you mean the "Application Layer" described by Evan's in the blue book? I'll assume you do, in which case the answer is that they should notreturn DTOs. I suggest reading chapter 4 in the blue book, titled "Isolating the Domain".

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

In that chapter, Evans says the following about the layers:

在那一章中,埃文斯对层说了以下内容:

Partition a complex program into layers. Develop a design within each layer that is cohesive and that depends only on the layers below.

将复杂的程序划分为多个层。在每一层内开发一个具有凝聚力且仅依赖于下面的层的设计。

There is good reason for this. If you use the concept of partial order as a measure of software complexitythen having a layer depend on a layer above it increases complexity, which decreases maintainability.

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

Applying this to your question, DTOs are really an adapter that is a concern of the User Interface / Presentation layer. Remember that remote/cross-process communication is exactly the purpose of a DTO(it's worth noting that in that post Fowler also argues against DTOs being part of a service layer, although he isn't necessarily talking DDD language).

将此应用于您的问题,DTO 实际上是用户界面/表示层所关注的适配器。请记住,远程/跨进程通信正是DTO目的(值得注意的是,在那篇文章中,Fowler 还反对将 DTO 作为服务层的一部分,尽管他不一定说的是 DDD 语言)。

If your application layer depends on those DTOs, it is depending on a layer above itself and your complexity increases. I can guarantee that this will increase the difficulty of maintaining your software.

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

For example, what if your system interfaces with several other systems or client types, each requiring their own DTO? How do you know which DTO a method of your application service should return? How would you even solve that problem if your language of choice doesn't allow overloading a method (service method, in this case) based on return type? And even if you figure out a way, why violate your Application Layer to support a Presentation layer concern?

例如,如果您的系统与多个其他系统或客户端类型接口,每个系统或客户端类型都需要自己的 DTO,该怎么办?你怎么知道你的应用服务的方法应该返回哪个 DTO?如果您选择的语言不允许基于返回类型重载方法(在本例中为服务方法),您将如何解决该问题?即使你想出了一个方法,为什么要违反你的应用层来支持表示层的问题呢?

In practical terms, this is a step down a road that will end in a spaghetti architecture. I've seen this kind of devolution and its results in my own experience.

实际上,这是一条以意大利面条式建筑告终的道路。我已经在我自己的经验中看到了这种权力下放及其结果。

Where I currently work, services in our Application Layer return domain objects. We don't consider this a problem since the Interface (i.e. UI/Presentation) layer is depending on the Domain layer, which is belowit. Also, this dependency is minimized to a "reference only" type of dependency because:

我目前工作的地方,应用层中的服务返回域对象。我们不认为这是一个问题,因为界面(即 UI/演示)层依赖于它下面的域层。此外,这种依赖被最小化为“仅引用”类型的依赖,因为:

a) the Interface Layer is only able to access these Domain objects as read-only return values obtained by calls to the Application layer

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

b) methods on services in the Application Layer accept as input only "raw" input (data values) or object parameters (to reduce parameter count where necessary) defined in that layer. Specifically, application services neveraccept Domain objects as input.

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

The Interface Layer uses mapping techniques defined within the Interface Layer itself to map from Domain objects to DTOs. Again, this keeps DTOs focused on being adapters that are controlled by the Interface Layer.

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

回答by Ilya Palkin

So far, we use domain models (mostly entities) across all layers, and we use DTOs only as view models (in controller, service returns domain model(s) and controller creates view model, which is passed to the view).

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

Since Domain Model provides terminology (Ubiquitous Language) for whole your application it is better to use Domain Model widely.

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

The only reason to use ViewModels/DTOs is an implementation of MVC patternin your application to separate View(any kind of presentation layer) and Model(Domain Model). In this case your presentation and domain model are loosely coupled.

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

Sometimes service needs to return data object that wasn't defined in the domain and then we either have to add new object to the domain that isn't mapped, or create POCO object (this is ugly, since some services return domain models, some effectively return DTOs).

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

I assume that you talk about Application/Business/Domain Logic services.

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

I suggest you return domain entities when you can. If it is needed to return additional information it is acceptable to return DTO that holds several domain entities.

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

Sometimes, people who use 3rd part frameworks, that generates proxies over domain entities, face difficulties exposing domain entities from their services but it is only a matter of wrong usage.

有时,使用在域实体上生成代理的第三部分框架的人在从他们的服务中暴露域实体时会遇到困难,但这只是错误使用的问题。

The question is - if we strictly use view models, is it ok to return domain models all the way to controllers, or should we always use DTOs for communication with service layer?

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

I would say it is enough to return domain entities in 99,9% cases.

我会说在 99.9% 的情况下返回域实体就足够了。

In order to simplify creation of DTOs and mapping your domain entities into them you can use AutoMapper.

为了简化 DTO 的创建并将您的域实体映射到它们,您可以使用AutoMapper

回答by Timo

If you return part of your domain model, it becomes part of a contract. A contract is hard to change, as things outside of your context depend on it. As such, you would be making part of your domain model hard to change.

如果您返回域模型的一部分,它将成为合同的一部分。合同很难改变,因为上下文之外的东西取决于它。因此,您将难以更改域模型的一部分。

A very important aspect of a domain model is that it is easy to change. This makes us flexible to the domain's changing requirements.

域模型的一个非常重要的方面是易于更改。这使我们能够灵活应对领域不断变化的需求。

回答by Niklas Wulff

Late to the party, but I'm facing the exact same type of architecture and I'm leaning towards “only DTOs from service”. This is mainly because I've decided to only use domain objects/aggregates to maintain validity within the object, thus only when updating, creating or deleting. When we're querying for data, we only use EF as a repository and maps the result to DTOs. This makes us free to optimize read queries and not adapt them to business objects, often using database functions as they are fast.

迟到了,但我面临着完全相同类型的架构,而且我倾向于“仅来自服务的 DTO”。这主要是因为我决定只使用域对象/聚合来维护对象内的有效性,因此仅在更新、创建或删除时。当我们查询数据时,我们只使用 EF 作为存储库并将结果映射到 DTO。这使我们可以自由地优化读取查询,而不是使它们适应业务对象,通常使用数据库功能,因为它们速度很快。

Each service method defines its own contract and is therefore easier to maintain over time. I hope.

每个服务方法都定义了自己的契约,因此随着时间的推移更容易维护。我希望。

回答by jnovo

I'd suggest analyzing these two questions:

我建议分析这两个问题:

  1. Are your upper layers (i.e. view & view models / controllers) consuming the data in a different way of what the domain layer exposes? If there is a lot of mapping being done or even logic involved I'll suggest revisiting your design: it should probably be closer to how the data is actually used.

  2. How likely is it that you deeply change your upper layers? (e.g. swapping ASP.NET for WPF). If this is highly unlike and your architecture is not very complex, you may be better off exposing as many domain entities as you can.

  1. 您的上层(即视图和视图模型/控制器)是否以与域层公开的方式不同的方式使用数据?如果要完成大量映射或什至涉及逻辑,我建议重新审视您的设计:它可能应该更接近于实际使用数据的方式。

  2. 您深入改变上层的可能性有多大?(例如,将 ASP.NET 换成 WPF)。如果这非常不同并且您的架构不是很复杂,那么您最好公开尽可能多的域实体。

I'm afraid it is quite a broad topic and it really gets down to how complex your system is and its requirements.

恐怕这是一个相当广泛的话题,它真正归结为您的系统及其要求的复杂程度。

回答by max_cervantes

In my experience, unless you are using an OO UI pattern (like naked objects), exposing the domain objects to the UI is a bad idea. This because as the application grows, the needs from the UI change and force your objects to accommodate those changes. You end up serving 2 masters: UI and DOMAIN which is a very painful experience. Believe me, you don't want to be there. The UI model has the function of communicating with the user, the DOMAIN model to hold the business rules and the persistence models deals with storing data effectively. They all address different needs of the application. I'm in the middle of writing a blog post about this, will add it when it's done.

根据我的经验,除非您使用 OO UI 模式(如裸对象),否则将域对象暴露给 UI 是一个坏主意。这是因为随着应用程序的增长,来自 UI 的需求会发生变化并迫使您的对象适应这些变化。你最终服务于 2 个 master:UI 和 DOMAIN,这是一个非常痛苦的经历。相信我,你不想在那里。UI 模型具有与用户通信的功能,DOMAIN 模型用于保存业务规则,持久模型用于有效地存储数据。它们都解决了应用程序的不同需求。我正在写一篇关于这个的博客文章,完成后会添加它。