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

9

300

I'm (re)designing large-scale application, we use multi-layer architecture based on 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) 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).

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).

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.

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.

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 though using particular pattern isn't "necessary", at least at the moment.

Keyboard answered 4/2, 2014 at 14:19 Comment(6)
For those guys who vote for close - please would you care to explain why do you want to close this question as opinion based?Keyboard
Because this IS opinion based. Good architecture maybe due to consensus, but it is still opinion. This is not a good fit for the Q&A format of SO, since it tends to generate debate. Try CodeReview.Camiecamila
@Camiecamila "Code Review is a question and answer site for sharing code from projects you are working on for peer review." - my question is not at all about code, so it would be off topic there; SO: "Focus on questions about an actual problem you have faced. Include details about what you have tried and exactly what you are trying to do." - I have specific expert problem, which I tried to solve. Could you please be more specific what's wrong with this question, since many questions here are about architecture and such questions are apparently ok, so I can avoid any further misunderstandings?Keyboard
@RobertGoldwein, don't mind the SO Close Mafia, your question is legit.Psych
@Camiecamila - A good many SO questions are opinion based by definition and that doesn’t decrease the value of the question and answer. Particularly any question that relates to design patterns.Cowan
this is one of the most important questions on software industry thanks manSordid
B
257

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 boundary; it encapsulates the domain. In other words, it protects the domain.

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?

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?

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.

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

This is one of the disadvantages 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, especially in a team environment where different teams are assigned to different layers. See WET principle.

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.

Why use a 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

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).

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.

Arguments Against DTO

Arguments For 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
Beilul answered 5/2, 2014 at 6:0 Comment(25)
Hi, thank you for your answer, it's really good summary for, and also thanks for the links. My sentence "Sometimes service needs to return data object that wasn't defined in the domain" was badly chosen, it means that service combines several DOs from one repository (e.g., attributes) and produces one POCO as a composition of these attributes (based on business logic). Again, thanks for really nice answer.Keyboard
@RobertGoldwein If the new POCO is part of the domain(aka business requirement), then shouldn't you warrant its own entity in the domain model? It doesn't have to be mapped if it is constructed per request. This POCO should be constructed in the domain layer, the service layer only maps the constructed POCO to its equivalent DTO for response. If you are wondering who should construct it, this "constructor" can be an aggregate root (parent entity), or a factory that lives in the domain.Beilul
Frankly now I'm not sure if such POCO - constructed by the service from domain models - belongs to the domain, or is only DTO. It's like your domain consists of all elements, your service returns specific molecules - water and ethanol; the service would return Alcohol (list of molecules, each molecule - water and ethanol - consists of several atoms and the list depends on parameters, e.g. ration water/ethanol, required volume, etc.), combining domain models (atoms), but molecule Alcohol itself doesn't belong to he domain - so Alcohol is just DTO, or "unmapped" domain object?Keyboard
Sorry, I was initially thinking of bounded contexts, but thats another topic. Yes, the service can construct the Alchohol. However, If the Alchohol exists in your system because of a business requirement then it should be a domain object (thus adding an equivalent DTO)Beilul
So you'd add Molecule as a domain object (but without any mapping to DAL)? In my example, MoleculeService would operate with/on Molecules, but since molecules are made up from atoms and their composition is given only by e.g. parameters, it has nothing to do with persistence, since only atoms are persisted - wouldn't make more sense to only create MoleculeDto (service layer) and MoleculeViewModel (presentation layer) without adding Molecule to domain? (anyway, I'm starting to like this molecule example :))Keyboard
Having entities in the domain model is not about persistence, its about creating a layer specifically for the subject matter of your application (this layer is your domain). Have you explored "Domain Driven Design"?Beilul
An important performance consideration is just how those domain models or DTOs are returned from your service. With EF, if you realize a query to return a concrete collection of domain models (with a .ToArray() or ToList(), for instance) you select all columns to populate the realized objects. If you project the DTO in the query instead, EF is smart enought to select only the columns required to populate your DTO, which can be significantly less data to transfer in some cases.Urmia
You can map your objects 'by hand'. I know, boring stuff, but takes 2-3 min per model and there is always a possibility to bring a lot of problems when you use a lot of reflection (AutoMapper etc.)Nod
"...then the presentation layer is tightly coupled to your domain, any changes in the domain requires you to change your controllers, etc, recompilation and everything." This happens also when you return DTOs. You domain changes because of new features. Your features change the DTOs your DTO`s change your controllers, etc, recompilation and everything. Thats a wrong assumption you do.Illogic
@Illogic - You are correct on that one. I removed the "recompilation". However, any changes in the domain doesn't mean the controllers have to change immediately. If I introduce a new field in a DTO, the controllers won't break as long as I left the existing fields untouched. If I need to change an existing field (renaming) I must keep the old field and create a new one (Fname and FirstName), then I can mark the old field as "Obsolete" so it can show as a "Warning" in Visual Studio to who ever is using it. Backwards compatibility, then remove old once everybody is in synced.Beilul
If we're reading the same article, it was actually Randy Stafford that said it on Fowler's blog. What he said was that the service layer encapsulates "the application's business logic", not "the domain". I think that's an important difference, at least as I'm interpreting it. For example, my service layer will pass back anything it wants to the caller (incl. domain objects), but the only way to modify the domain objects is through the service layer since it controls interaction with the persistence layer. Beyond that, the domain model protects itself by design. Would you disagree?Melee
thanks for answering so simply and with such content. You did a favor to me, and made my life much simpler and happy, thank you.Fantasy
We had a 10 million project get canceled because it was to slow... Why was it slow ? Transferring the DTO object all over the place using refelection. Be careful. Automapper also uses reflection.Mastership
@Beilul "Sometimes service needs to return data object that wasn't defined in the domain" - Another example for this is a ProductDTO having wishlist/favourite parameter which will get populated based on the logged in user selection. In this case when fetching ProductDTO we may need to refer ProductRepository/ProductDAO as well as ProductWishlistRepository/ProductWishlistDAO. Is the code logic going to be in Service layer for making this aggregated response?Palma
@Mastership Automapper (as well as other modern mapping tools - ORMs, Dapper, etc.), doesn't use reflection every time it copies data from one object to another. Rather, it uses reflection just once per mapping from one class to another. It compiles a method on the fly that maps between objects of the given types and uses that cached function repeatedly. There's essentially no speed difference between using Automapper and hand-written mapping code. Meanwhile, the main point of Fowler's original DTO concept was to consolidate costly requests (web, db, etc.) for performance.Esophagitis
@MarredCheese, Is there any doc's that support that statement? We had an array of search results of one type of object "A" and were converting it to an array of type "B". Our arrays were 100 - 1000 items if I recall. Granted I don't have docs that support my statement but when we timed things the delay was significant. Any time reflection is used in a loop will kill ya.Mastership
@Mastership Strangely, I can't find any direct mention of the mapping functions being pre-compiled in the official documentation. But that's how it seems to work based on looking at the source code. However, it seems that it didn't always work that way, because the author noted a huge speed improvement upon the release of version 5 here. So I think you're right that it used to be really slow, but it's been fixed since then.Esophagitis
@MarredCheese, That's interesting. Before it was deathly slow if you wanted to map more that one object. I wonder how it works now and if it still uses reflection. Nothing is compiled using reflection... by definition it's a dynamic thing that occurs at runtime. I haven't used version 5.Mastership
@Mastership LambdaExpression.Compile (or System.Reflection.Emit) can generate compiled code at runtime. A modern mapper doesn't use reflection each time it populates an object. Instead, at startup, 1) it uses introspection to examine the classes to be mapped from/to, 2) it builds an expression tree that describes how to achieve that mapping, and 3) it compiles that expression into a fast, reusable function.Esophagitis
@MarredCheese, I'm not sure about your last statememt "Generate compiled code at runtime". People use the term "Compiled" loosely now days. I'm glad it's fast for you now. It's easy to add a timer to test this. Cheers.Mastership
@Mastership You can go look at the Automapper source code and see expressions being compiled. That generates Common Intermediate Language (CIL) code, which is the same thing the C# compiler does when you build your project. If that's not "compiled," then no C# code is.Esophagitis
what about internal services? by internal services I mean those services that are only used by other services (not by external users), because sometimes the service layer itself needs to be separated into sub-layers, for example, there are some core services and some higher-level services that depend on these lower-level core services.Darryl
I beg to disagree from the central answer. For me it's clear that if the application layer builds a DTO as a response to an above/outer layer (e.g. the controller or view) it would know something about that layer and that goes against the dependency rule. Furthermore, even with a DTO, almost any change of an entity attributes would require that dependent objects were changed. As the domain is the core, we shouldn't worry about that.Corder
When talking about a DTO, do we mean an API response body returned to the client? If yes, what exactly is the point of returning a DTO from the service then? With this approach: If we have to provide different versions of an API over time, we always have to provide a corresponding service method returning different DTO's. Why not simply do the mapping from domain object to different DTO's in the controller? And more important: What if other services need the actual domain object to do further processing? They don't need a DTO which is actually a representation for the clientIdellaidelle
@Äxel - Nope. This is for the "application service".Beilul
T
46

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 not return DTOs. I suggest reading chapter 4 in the blue book, titled "Isolating the Domain".

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 complexity then 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).

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.

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?

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 below it. Also, this dependency is minimized to a "reference only" type of dependency because:

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

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 never accept Domain objects as input.

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.

Twentytwo answered 19/12, 2016 at 20:53 Comment(10)
Quick question. I'm currently spinning on what to return from my application layer. Returning domain entities from the application layer feels wrong. Do I really want to leak the domain to the "outside"? So I was contemplating DTOs from the application layer. But that adds another model. In your response you said you return Domain models as "read-only return values". How do you do that? I.e., how do you make them read only?Formication
I think I'm just going to adopt your position. Application services return Domain models. The port adaptor layers (REST, presentation, etc) then translate them into their own model (view model or representations). Adding a DTO model between the application and port adaptors seems overkill. Returning domain models still adheres to DIP, and domain logic stays inside the bounded context (not necessarily inside the application boundary. But that seems like a fine compromise).Formication
@MichaelAndrews, glad to hear that my answer helped. Re: your question about the returned objects being read-only, the objects themselves aren't truly read-only (i.e. immutable). What I mean is that it doesn't happen in practice (at least in my experience). To update a domain object the Interface Layer would have to either a) reference the domain object's repository, or b) call back into the Application Layer to update the object it just received. Either of these are such clear violations of good DDD practice that I find them to be self-enforced. Feel free to upvote the answer if you care to.Twentytwo
This answer is very intuitive to me for several reasons. First, we can reuse same Application Layer for several UIs (APIs, Controllers) and each one can transform the model as it sees fit. Second, if we were to transform model-to-DTO in App. Layer, that would mean DTO is defined in App. Layer, which in turn means DTO is now part of our Bounded Context (not necessarily Domain!) - this just feels wrong.Myeshamyhre
I was just about to ask you a follow-up question and then saw you already answered it: "application services never accept Domain objects as input". I would +1 again if I could.Myeshamyhre
If you update some properties of an aggregate (in controller) and want to save the changes into the database, what input will be in your service CustomerApplicationService.Save(<input>) if you dont know which properties where changed (the customer can have tens of properties) ?Sheldon
Theoretically I agree 100% with you, but in practice, currently I'm having an issue, maybe you can help me?! I have to do some calculations before returning the response, also have to call a service which does a remote call to another service, then where should I do these stuff if not on the service and map to DTO? On Controller? Then it grows quite big then, doesn't it?Molybdate
What about the input in case of including multiple parameters (value objects) do I create a command/dto for the input in service layer ?Waybill
I did add an up-vote for this answer but @Molybdate as well as the input of many parameters question I asked seems encoureging enough to use dto customized for service layer. The facade/adapter&port will handle mapping for their own usage to outer layers. More work but solid isolation imho. Don't you agree ?Waybill
Could you please explain why you wrote "application services never accept Domain objects as input"? Especially for application services (I assume this is the business logic layer, and with services I mean business logic classes) that call each other - why can't they use domain models to call each other?Hammers
T
16

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?

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.

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.)

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.

Good luck!

Technetium answered 5/7, 2014 at 10:10 Comment(0)
B
15

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.

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.

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 Response pattern in your Service layer), cheers!

Backandforth answered 4/2, 2014 at 18:6 Comment(1)
sorry about that! you can see it here ehsanghanbari.com/blog/Post/7/…Backandforth
G
9

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.

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

Gunther answered 17/11, 2019 at 17:56 Comment(5)
After years we came to the same conclusion, exactly for reasons you mentioned here.Keyboard
@RobertGoldwein That's great! I feel more confident in my decision now. :-)Gunther
@NiklasWulff: so, the Dtos in question are now part of Application Layer contract, i.e. are part of core/domain. What about the Web API return types? Do you expose the Dtos mentioned in your answer or do you have dedicated View Models defined in the Web API layer? Or to put it differently: do you map the Dtos to View Models?Myeshamyhre
@Robotron We don't have Web API, we use MVC. So yes, we map the dto:s to different view models. Often a view model contains lot of other stuff to display the web page, so the data from the dto:s only make up a part of the view modelGunther
Having some (pseudo) code to read would be very helpful...Kursk
S
8

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).

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 pattern in 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.

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).

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.

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?

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

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

Sunshine answered 4/2, 2014 at 18:14 Comment(0)
H
6

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.

Hubblebubble answered 24/10, 2019 at 8:56 Comment(0)
F
3

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.

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

Frostwork answered 4/2, 2014 at 16:13 Comment(2)
In our case, upper layer surely won't change. In some cases, service returns quite unique POCO object (constructed from more domains - e.g., user and files he owns), in some cases a service returns simply domain model - e.g., result of "FindUserByEmail() should return User domain model - and here's my concern, sometimes our service(s) returns domain model, sometimes new DTO - and I don't like this inconsistency, I read as much articles as I could and majority seems to agree that even though the mapping Domain Model <-> DTO is 1:1, domain model shouldn't leave service layer - so I'm torn.Keyboard
In such an scenario and provided that you can bear the extra development effort I'd also go with the mapping so that your layering is more consistent.Frostwork
D
3

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.

Devonna answered 5/12, 2018 at 18:28 Comment(2)
Did you manage to write the blog post concerning this ?Smokechaser
Interested in your blog post as wellKursk

© 2022 - 2024 — McMap. All rights reserved.