Note: read Part II of this post here.
A Data Transfer Object (DTO) is an object intended to carry data, for example between the client and the server or between the UI and the domain layer. It exposes fields or properties (getters and setters) publicly. Sometimes a DTO could be seen as an anemic model. DTOs are mostly used out of the hexagon, in the delivery mechanism.
On the other hand domain models should promote a good object oriented design with proper encapsulation. They belong in the hexagon.
However it's not always clear where to use a DTO and a domain model, where and how to convert one into the other. None of the approaches we have tried are working for us in all the cases, there are always exceptions. But there are some principles that are helping me:
Principle: Domain models may travel out of the hexagon but... do not bind domain models to the GUI directly.
Reason: Data mappers like XAML binding need public setters and getters. We don't want public setters in our domain models. When the GUI is defined with XAML, the data binding is declared in this XML. Changing the visibility of a getter from public to private will compile but as soon as the user enters some data in a bound field it will throw a runtime exception that we can't reproduce with automated tests, it's hard to discover.
Principle: Apply data transformations out of the core hexagon
Usually objects start with one or two fields and no logic on them, they are anemic models. At that stage there is no difference with a DTO.
Later on, as we learn about the domain and the objects grow, we need to apply transformations to carry data over to the view or through the network. Our first approach was to populate the DTO from the model:
- var dto = domainModel.ToDto(); // model to dto
- var model = DomainModel.From(dto); // model from dto
But as many people commented on this post and as we saw later, it's not a good idea because we are coupling the domain models to the way data is exported and sent out of the hexagon. Coupling is unavoidable, what matters is the direction of the coupling. It's OK that the outside world knows the model but it's not that good to pollute our core domain with external concerns like GUIs or serialization.
Our current approach is to have an "adapter" class that knows both worlds and connect them:
- var dto = adapter.ToDto(model); // model to dto
- var model = adapter.ToModel(dto); // model from dto
Thanks everyone for the comments