Differing Opinions: DTOs vs Domain Objects
Since the introduction of NHibernate and WCF, .NET developers have been moving closer and closer to the concept of unified entity-models. The end game here is that the same class can be act as your ORM entity, your WCF DTO, and your model for a MVC, MVP, or MVVM framework. Mark Seemann, author of Dependency Injection in .NET, argues this isn’t necessarily a good thing.
The crux of his argument is that “At the Boundaries, Applications are Not Object-Oriented”. As you are undoubtedly aware, most serialization technologies require public, default constructors and writable properties. These requirements essentially force you to break encapsulation and the principals of data hiding when designing your DTOs. Even basic invariants such as requiring fields to be non-null/non-empty are impossible to enforce because the DTO could literally omit anything. He goes on to justify these two tenants:
- Services share schema and contract, not class.
- DTOs don’t break encapsulation because they aren’t objects at all.
Given this situation, Mark offers three options for moving forward:
One option is to stick with what we already have. To bridge the gap we must then develop translation layers that can translate the DTOs to properly encapsulated domain objects. This is the route I take with the samples in my book. However, this is a solution that more and more I’m beginning to think may not be the best. It has issues with maintainability. (Incidentally, that’s the problem with writing a book: at the time you’re done, you know so much more than you did when you started out… Not that I’m denouncing the book – it’s just not perfect…)
Another option is to stop treating data as objects and start treating it as the structured data that it really is. It would be really nice if our programming language had a separate concept of structured data… Interestingly, while C# has nothing of the kind, F# has tons of ways to model data structures without behavior. Perhaps that’s a more honest approach to dealing with data… I will need to experiment more with this…
A third option is to look towards dynamic types. In his article Cutting Edge: Expando Objects in C# 4.0, Dino Esposito outlines a dynamic approach towards consuming structured data that shortcuts auto-generated code and provides a lightweight API to structured data. This also looks like a promising approach… It doesn’t provide compile-time feedback, but that’s only a false sense of security anyway. We must resort to unit tests to get rapid feedback, but we’re all using TDD already, right?
If you are interesting in Object Oriented Design and Encapsulation don’t miss his series titled Poka-yoke Design: From Smell to Fragrance.
I question your generalisation
I'm not sure thats correct. Many/most of the .NET developers I've been following in the last few years have been arguing that this often isn't a good idea (other than for trivial cases). So I'm not sure we are moving closer to this end-game at all, in fact I'd hope the average .NET developer today is advanced enough to not walk straight into that anti-pattern.
Re: I question your generalisation
OOP is not successor of SP, but part of it
Also, if our world is modelled at the easiest by values and actions, there's nothing wrong with making this distinction; I don't get why it is an anti-pattern.
There are times when "OOP" models are better, but a lot of applications simply cannot enclose actions to the values they operate on; we could pretend such is not true, but still, we've learnt for decades that the distinction of action vs. value is alive.
Let's stop pretending it.
Thanks for heads up
Also contrast "one DTO to rule them all" with CQRS and "use domain model for updates only" approaches and many writings on the subject. There are good reasons to have both and there are very good reasons to let external parties/requirements to drive the definition of DTOs.
Personally I have high expectations for automated translation between domain model and DTOs and keep an eye on proto-buffers for the purpose.
Todd Montgomery Dec 19, 2014