Think Before You Use The DTO Pattern
Let me preface this blog post by saying that there are still times when a DTO makes sense. Also, this post is written from a .NET perspective, so some things may be different on your platform. What I want to address though is the tendency of many developers to just automatically create a set of DTOs for each layer for each domain model. As I mentioned in other blog posts, you should always think about why you are doing something before you are doing it.
Reasons like “this is the way we have always done it” or “this is the best practice” do not stand up to scrutiny. A dispassionate assessment of an approach should be taken before engaging in a practice. You should also be able to defend your choice in a technical debate without falling back on regurgitated lines like “I’m all about clean code.”
The DTO Pattern is an Anti-Pattern in Most Cases
I used to use DTOs a lot too about 4 or 5 years ago along with the repository pattern. I had been conditioned to never allow your domain models to cross layers and to keep everything separated. I was one of the lemmings following after what everyone had done before me, never thinking about why I was doing it. Automapper was the life blood of the application as I mapped to view models, data models, api models, and so on and so forth. New layer? New set of DTOs! All those duplicated classes, all that mapping, all that suffering and pain wrought upon the application in the name of the holy grail: separation.
Thankfully, my approach evolved over time and I began to question why I was going through all of this. Was it really worth while to incur the cost of extra classes and mapping just so I could say I wasn’t using my domain models with entity framework? How about my API? Should I really be calling everything a DTO? Is half my application calling Automapper really a good practice?
View Models vs DTOs
I don’t consider View Models to be DTOs. View Models can encapsulate logic, validation, or anything else you might include that is specific to the view. Your view model may very well end up with most, if not all of the properties of the domain model that it is built on. More often than not though, the view model only contains what is needed for the view to function. I have, however, seen people use DTOs as their view models. A view model is not a DTO, and you especially don’t need to map from a view model to a DTO and then to a Domain model. When MVC and Razor were still all the rage, it was very common to use automapper to map from a View Model to a Domain Model. This is a perfectly valid approach that still makes sense if you are using Razor. Just as you should not use a DTO for your view, you should not use a domain model either.
Most new applications have a harder separation between the server and the client. The rise of React, Angular, and Vue now removes the old approach of a C# view model that could be directly mapped to a domain model. In addition, the rise of JSON based ASP.NET Web Api and the subsequent decline of WCF have further reduced the need for DTOs.
ASP.NET Web Api and WCF
In the days of WCF, there was a rational argument for the usage of DTOs. You probably didn’t see the DTO suffix on these objects and instead are more familiar with the typical Request and Response suffixes for WCF endpoints. On this note, I want to digress for a moment and discuss the DTO suffix in general: it sucks. It is perhaps the least descriptive suffix ever created. There was a certain fortune 100 that I worked with a number of years ago who decided to suffix all of their WCF requests and responses with “DTO.” Unfortunately these WCF services all worked with the same domain, and they had a lot of them. So what happened when an application ended up using multiple WCF services in the same class? Name collisions everywhere followed by aliasing everything.
At the time I tried to explain the problem with the situation with my Harry Potter example. Imagine if the author of Harry Potter had gotten lazy and decided to name all the characters Harry Potter. She needed a way to differentiate between them though so she prefixed each one with a number. That is essentially what you are doing when you suffix all your DTOs as “DTO.” Writing software is just like writing a book. It needs to be descriptive, and ultimately it needs to tell a story. The solution name is your book title, the projects your volumes, the classes your chapters, and the methods your paragraphs. Reading a book with everyone named HarryPotterDTO is just as annoying as having to read your class that has six different OrderDTOs from six different WCF service proxies.
Web Api is a Medium to Interact With the Domain
I advocate using your domain classes directly in your API. The reason is that if someone is interacting with your API then they are ultimately interacting with your business and all actions on the API represent that. Using your domain class does not mean that you have to allow all properties to be modifiable. You still have complete control over everything, and in terms of publishing what your API accepts, tools like swagger let you control what parts of the domain are published as your contract. There are a number of different ways to control this including using something like request post processing with ASP.NET filters. The only time I advocate the usage of a DTO is when calling third party APIs or APIs that reside outside of the domain that your application is providing.
What About Persistence Ignorance?
Simply using your domain models as your entities with Entity Framework does not automatically pollute the domain with persistence implementation details. As long as you keep your entity framework configuration and context in a separate assembly, then there is no reason your domain needs to be aware of its existence. As I have discussed before, Entity Framework is already an abstraction of your persistence implementation that provides you a generic repository pattern and a unit of work pattern. I have seen certain blogs advocate that your data model is not your domain model. From a principle standpoint, this is true, but from a practical standpoint having a completely separate list of data models that map to your domain has little gain in today’s world. In the event you have a table that truly has no representation in your domain, then by all means create a DataModel for it. Don’t resort to a DTO suffix and don’t blindly do it for every table. Use DTOs responsibly and you’ll be happier for it.