DEV Community

Cover image for Alternatives to DTO
Nicolas Fränkel
Nicolas Fränkel

Posted on • Originally published at blog.frankel.ch

Alternatives to DTO

More than a decade ago, I wrote about the DTO:

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, where each call is an expensive operation. Because the majority of the cost of each call is related to the round-trip time between the client and the server, one way of reducing the number of calls is to use an object (the DTO) that aggregates the data that would have been transferred by the several calls, but that is served by one call only.

-- Wikipedia

I believed (and still do) that it should be a thing of the past. Yet, it seems its usage is still widespread.

I do not deny there are some valid reasons to transform data. However, there are alternatives to the traditional DTO process:

  1. Return a business object from the service layer

    Note that projects I've worked on previously, we directly mapped the BO to the entity read from the database.

  2. Transform the BO to a DTO in the presentation layer

  3. Return the DTO from the presentation layer

Return the entity itself

When the entity's properties are a superset of the properties that need to be displayed, aggregating additional properties is not required. Transforming the entity to a DTO is not only overkill. It hinders performance.

In that case, the best approach is to return the entity itself.

JPA projection

We make requests for specific data in a particular context. Thus, when the call reaches the data access layer, the scope of the required data is fully known: it makes sense to execute a SQL query that is tailor-fitted to this scope.

For that, JPA offers projections. In essence, a projection in a query allows selecting precisely the data one wants. Here's an example; given a Person entity class and a PersonDetails regular class:

CriteriaQuery<PersonDetails> q = cb.createQuery(PersonDetails.class);
Root<Person> c = q.from(Person.class);
q.select(cb.construct(PersonDetails.class,
  c.get(Person_.firstName),
  c.get(Person_.lastName),
  c.get(Person_.birthdate)
));
Enter fullscreen mode Exit fullscreen mode

Jackson converter

Regarding JSON specifically, we can delegate the process of providing the correct data to the serializer framework, e.g. Jackson. The idea behind it is the following: the main code processes the entity as usual, and at the edge, a Jackson converter converts it to the required JSON structure.

If less data is necessary, it's child's play. If more, then the converter needs additional dependencies to get data where it is. Of course, if this data comes from the same datastore, this is not great, and the alternative above is more relevant. If not, it's an option.

GraphQL

Last but not least, one could return the full-blown entities and let the client decide what data makes sense in its context.

GraphQL is built around this idea: Facebook created it, and it is now fully Open Source. Its main advantage is to offer a specification and a lot of language-specific implementations on top of it.

A query language for your API

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

-- GraphQL website

Conclusion

When a gap exists between the business and presentation models, it's easy to get back to age-old "patterns" such as the DTO. However, any of the alternatives above are probably more relevant.

To go further:

Originally published at A Java Geek on March 6th, 2022

Top comments (1)

Collapse
 
nandorholozsnyak profile image
Nándor Holozsnyák

I think it is always a debate, if you are trying to maintain an application architecture that should last for years, because of the length of the project, you should apply conventions and standards. Of course, mapping objects between different types back and forth could be big performance killing stuff, but I do really like using different models for different API interfaces. For REST people are using entities/DTO project types for return types but you expose your internal domain in these cases, and you can easily break clients if you are not taking attention.

For example with gRPC it could be a bit complex, cause gRPC creates generated classes for you and you MUST map it every time you want to leave your domain, you will not use these classes in you business login / application domain (or at least this is what I do) - I map all day long :D
And if you for example like code generation (like in case of gRPC), you can generate your REST controllers and their request/response classes and models from an OpenAPI file (I kinda like it but that is another topic), and in cause you MUST map again your business objects to the final response objects.

I see the downsides of it, but if people just start to use different "styles" in a long running project, it can make it a mess.