If you have an entity as in a DDD-entity (not what is generally called an entity in frameworks such as entity framework) then the entity should protect its invariants. Thus using the entity as a DTO is most likely wrong. A DTO is unvalidated input data from the user and should be treated as such. An entity is validated data in one of the object types valid invariants. Thus you should have a conversion between a DTO and an entity that converts a validated DTO to a domain entity object.
In many cases people skimp on these by using anemic domain models (http://www.martinfowler.com/bliki/AnemicDomainModel.html) where the "entities" generally contain no logic and then they put all the invariant protection logic etc in external code (and thus need to check invariants all over the place). This can work, but imho it leads to bugs. But it's something that happens pretty much everywhere - especially since a lot of frameworks encourage this by making it super easy to write code this way. This is imho too bad since it leads to the easiest/fastest way of coding not being the one that leads to the best results (if you follow DDD that is)...
But it's also important to recognise that in many cases DDD might be overkill. If you are basically making a CRUD application (which in many cases you probably are) then DDD might be overkill for your domain. Of course this doesn't mean that you should throw away all the good ideas and just go into full-on "do-anything mode", but in some cases not caring about certain parts of DDD can be the correct choice to make. The hard trick here is, of course, to identify if your domain is complex enough to warrant DDD or if it's easy enough to not bite yourself in the ass by foregoing certain parts of it. :)
Ok. Problem 1: getting DTO's from entities:
Since your entities can expose their data publicly you can access their properties and instantiate DTO object or simply serialise the entity directly
Problem 2 : Entities from DTO's:
A constructor method which takes a list of the properties to be set can be called using the properties of the DTO
Problem 3 : large entities when you only need summary info
Create a new summary object which you can retrieve from the repository. Note I suggest you make strongly typed repositories with methods like Repo.GetMyObjectById(string id) rather than expose a generic ORM.
Problem 4 : where to orchestrate all this.
My recommendation is to have a service class one level below your hosting service/app/website.
This has access to the repositories DTO's and entities and its methods map to the controllers/service calls of your application so that you do not need any code at the top level and can host the same service in multiple ways.
Giving it access to the repos is not an issue when you can only use them to retireve entities rather than doing any query they like.
Putting this assembly/orchestration logic inside the entity is usually bad as you will want the entity to be reused for other purposes.
This top level service should be very light. Just, get these objects, call this method, create and return the result/DTO/viewmodel
Because its so light its not a massive prob if you skip the layer and put the code in your controller. But it will save you time if you change the hosting layer and help with testing etc
Best Answer
Whenever a developer asks "what's the point of doing this?", what they really mean is "I see no use case where doing this provides a benefit". To that end, let me show you a few examples.
All examples will be based on this simple data model:
And you can assume that the application uses this data in many varied ways (reports, forms, popups, ...).
The entire application already exists. Everything I mention is a change to the existing codebase. This is important to remember.
Example 1 - Changing the underlying data structure - Without DTO
The requirements have changed. The age of the person needs to be dynamically retrieved from the government's database (let's assume based on their first and last name).
Since you don't need to store the
Age
value locally anymore, it therefore needs to be removed from thePerson
entity. It's important here to realize that the entity represents the database data, and nothing more. If it's not in the database, it's not in the entity.When you retrieve the age from the government's web service, that will be stored in a different object (or int).
But your frontend still displays an age. All the views have been set up to use the
Person.Age
property, which now no longer exists. A problem presents itself: All views that refer to theAge
of a person need to be fixed.Example 2 - Changing the underlying data structure - With DTO
In the old system, there is also
PersonDTO
entity with the same five properties:Id, FirstName, LastName, Age, CityId
. After retrieving aPerson
, the service layer converts it to aPersonDTO
and then returns it.But now, the requirements have changed. The age of the person needs to be dynamically retrieved from the government's database (let's assume based on their first and last name).
Since you don't need to store the
Age
value locally anymore, it therefore needs to be removed from thePerson
entity. It's important here to realize that the entity represents the database data, and nothing more. If it's not in the database, it's not in the entity.However, since you have an intermediary
PersonDTO
, it's important to see that this class can keep theAge
property. The service layer will fetch thePerson
, convert it to aPersonDTO
, it will then also fetch the person's age from the government's web service, will store that value inPersonDTO.Age
, and passes that object.The important part here is that anyone who uses the service layer doesn't see a difference between the old and the new system. This includes your frontend. In the old system, it received a full
PersonDTO
object. And in the new system, it still receives a fullPersonDTO
object. The views do not need to be updated.This is what we mean when we use the phrase separation of concerns: There are two different concerns (storing the data in the database, presenting data to the frontend) and they need a different data type each. Even if those two data types happen to contain the same data right now, that might change in the future.
In the given example,
Age
is a difference between the two data types:Person
(the database entity) doesn't need anAge
, butPersonDTO
(the frontend data type) does need it.By separating the concerns (= creating separate data types) from the beginning, the codebase is much more resilient to changes made to the data model.
I could give you more examples but the principle will always be the same.
To summarize
Person
)Name
. But just because they all have aName
property, doesn't mean that we should make them inherit from a sharedEntityWithName
base class. The differentName
properties have no meaningful relation.Name
gets renamed toTitle
, or a person gets aFirstName
andLastName
), they you'll have to spend more effort undoing the inheritance which you didn't even need in the first place.As a rule of thumb to consider separating concerns, think of it this way:
Suppose that every concerns (the UI, the database, the logic) is handled by a different person in a different location. They can only communicate by email.
In a well-separated codebase, a change to a particular concern will only need to be handled by one person:
If all of these developers were using the same
Person
entity, and a minor change was made to the entity, everyone would need to be involved in the process.But by using separate data classes for every layer, that issue isn't as prevalent:
PersonDTO
object, the business and UI dev do not care that he changed how the data is stored/retrieved.The key phrase here is since it doesn't affect them. Implementing a good separation of concerns seeks to minimize affecting (and therefore having to involve) other parties.
Of course, some major changes can't avoid including more than one person, e.g. when an entirely new entity is added to the database. But don't underestimate the amount of minor changes that you have to make during an application's lifetime. Major changes are a numerical minority.