Are DTO still mandatory with annotation, Spring-data and Jackson ?

Baptiste Thery
norsys-octogone
Published in
4 min readSep 21, 2022

DTO: state of the art

As you may already know, DTO is a common design pattern to separate your business logic and the technical layers. The main goal is to create an object that fulfills the technical layers needs, without impacting the business object that has a structure adapted to the business logic needs. To do so, you need 4 mappers. The first 2 are mandatory and provided by Spring and Jackson: they allow you to map your DTO to technical layer able to communicate with external systems (in most case JSON data). The other 2 mapper will be written by the developer (possibly using a library) in order to transform DTO into object used in business logic.

The main drawback of this approach is that you will have to use 2 extras mapper to convert your DTO to business logic objects.

Why do we use DTO ?

In short: DTO allows you to keep your business object structure free of technical requirements. One way to make a clean architecture is to avoid modifying business objects structure to meet technical requirements. You also want to avoid injecting technical algorithm and code into business object. When mixing technical and business layers, any change in external/technical layers requires to change your business layers that may introduce regression. This refers to the well-known programming principle SRP: an object should do only one thing and that is why it should not contain technical and business logic.

Adding behavior using Metadata

Spring web and Spring data allow you to inject data in your class using annotation. Below we explain how it works and why it respects the SRP, using a simple example.

This is where annotations come into action. Annotations are “static metadata” that you can add to your source code. That means an annotation actually does nothing by itself, it just gives additional information about an existing field, method, class… How does it work ? By using reflection, you can access annotation at runtime and execute some algorithms depending on the annotation. Doing so, you can modify the inner data and behavior of an object without adding logic to the object. Here is an example :

In this example the “main” code can inject the String “myValue” in “TheBusinessClass.wantToBeMapped” using reflection and the annotation “PleaseMapMe”. Hence the class “TheBusinessClass” does not undergo algorithmic modifications that impact the business logic. The annotation “PleaseMapMe” is just static metadata, it adds information on the field “wantToBeMapped”. This way, we inject data from an external algorithm and the single responsibility principle is satisfied in “TheBusinessClass” because the technical logic is in the “TheTechnicalClass” class.

Can we respect the SRP thanks to jackson ?

By default Jackson ask you to expose getters for every field you want to return as JSON and Setter for every field you want to complete with JSON. Doing so break the SRP because you add non business behavior in your object. For example: “cat.getLegs()” can not really be considered a functionality while “cat.walk()” is one. Fortunately this default features can be changed. “ObjectMapper.setVisibility” method allows you to change how jackson read the object. And “@JsonCreator” allows you to indicate to jackson to use a constructor to build the object. This way you can respect the SRP and java best practices while not using DTO.

Some more tips about inheritance

There is a lot more features to customize jackson and spring-data. The first question you may have should be about inheritance. For this, Jackson expose “@JsonTypeInfo” and “@JsonSubTypes” and spring data “@Inheritance” and “@DiscriminatorFormula”. A more complete example on Jackson and Spring-data usage will be released next time.

Conclusion

The usage of DTO is no longer mandatory, but the main drawback of using annotations instead is that your code could end up with a lot of annotation. Furthermore there are still some limitations in the usage of annotation, for example presently it is impossible for a class to contain two “@JsonCreator” meaning it is impossible to have two different constructors used by Jackson.

So when to use DTO? As always, it depends. Personally, I try to avoid using DTO, as it will add a lot of boilerplate code. If for some reason I have to create DTO, I do it using records.

--

--

Baptiste Thery
norsys-octogone

Techlead, passionate about software design and technological challenges.