Data Transfer Objects
There's a saying that
"If all you have is a hammer, everything looks like a nail."
When I first learned about Data Transfer Objects, I used them at every opportunity. I’ve since come to recognize this behavior as a form of "cognitive bias or overgeneralization" — when a developer tries to solve every problem with the same framework or design pattern, even when another approach might be more suitable.

In this post, I'll be sharing my experience working on different projects using DTOs, what I have learned and why it does not fit every project.
Definition
According to Wikipedia, a data transfer object:
- Is an object that carries data between processes
- The motivation for its use is that communication between processes is usually done resorting to
remote interfaces(e.g., web services), where each call is an expensive operation

The keywords being, carrying data between processes, communication via remote interfaces where each call is an expendive operation. It was originally created to solve the problem of remote communication overhead. But DTOs have evolved to serve other purposes, especially in modern application architectures e.g Domain Driven Design.
Past experience
At a former job, we used dtos to transfer data between layers like a Controller/Form Request to a Application Service, Domain Services etc. Looking back now, I see this did not align perfectly with the original, performance-focused wikipedia definition, it however provided significant architectural benefits:
Security and Data Shaping
- DTOs explicitly define what data is allowed to come into an application (input DTOs) and what data is exposed out of an application (output DTOs).
- DTOs tailored to specific use cases, ensuring a service only returns the necessary fields for a particular client view, optimizing payload size and reducing exposure of sensitive data
Decoupling and Abstraction
- DTOs decouple the external representation of data (what the API/form/user sees) from the internal representation (your domain objects/entities).
Summary
- They are data structures for I/O
- They don't enforce domain invariants (the domain model does that)
- They are merely vehicles for moving data to and from the rich Domain Model