REST API - DTOs or not? [closed]
Asked Answered
M

4

211

I am currently creating a REST-API for a project and have been reading article upon article about best practices. Many seem to be against DTOs and simply just expose the domain model, while others seem to think DTOs (or User Models or whatever you want to call it) are bad practice. Personally, I thought that this article made a lot of sense.

However, I also understand the drawbacks of DTOs with all the extra mapping code, domain models that might be 100% identical to their DTO-counterpart and so on.

Our API is mostly created so that other clients may consume data, however if we do it right we would also like to use it for our own web GUI if possible.

The thing is that we might not want to expose all the domain data to the other client users. Much of the data will only make sense in our own web application. Also, we might not want to expose all data about an object in all scenarios, especially relationships to other objects and so on. For example, if we expose a list of a particular object we would not necessarily want to expose the entire object hierarchy; so that the object's children will not be exposed, but can be discovered through links (hateoas).

How should I go about solving this problem? I was thinking about using Jackson mixins on our domain models to control what data would be exposed given different scenarios. Or should we just use DTOs all the way - even given its drawbacks and controversy?

Marquardt answered 23/3, 2016 at 9:40 Comment(3)
Don't be surprised if this question gets closed. It's more of a discussion based question which means there is no clear correct answer. Ask different people and you'll get a different answer.Albino
That article link (ibm.com/developerworks/community/blogs/barcia/entry/…) is broken.Ilario
@Ilario That's a great article and it's a pity it's no longer available. Here is the Web Archive cached version.Insanity
I
324

Why you should use DTOs in your REST API

DTO stands for Data Transfer Object.

This pattern was created with a very well defined purpose: transfer data to remote interfaces, just like web services. This pattern fits very well in a REST API and DTOs will give you more flexibility in the long run.

The models that represent the domain of your application and the models that represent the data handled by your API are (or at least should be) different concerns and should be decoupled from each other. You don’t want to break your API clients when you add, remove or rename a field from the application domain model.

While your service layer operates over the domain/persistence models, your API controllers should operate over a different set of models. As your domain/persistence models evolve to support new business requirements, for example, you may want to create new versions of the API models to support these changes. You also may want to deprecate the old versions of your API as new versions are released. And it’s perfectly possible to achieve when the things are decoupled.


Just to mention a few benefits of exposing DTOs instead of persistence models:

  • Decouple persistence models from API models.

  • DTOs can be tailored to your needs and they are great when exposing only a set of attributes of your persistence entities. You won't need annotations such as @XmlTransient and @JsonIgnore to avoid the serialization of some attributes.

  • By using DTOs, you will avoid a hell of annotations in your persistence entities, that is, your persistence entities won't be bloated with non persistence related annotations.

  • You will have full control over the attributes you are receiving when creating or updating a resource.

  • If you are using Swagger, you can use @ApiModel and @ApiModelProperty annotations to document your API models without messing your persistence entities.

  • You can have different DTOs for each version of your API.

  • You'll have more flexibility when mapping relationships.

  • You can have different DTOs for different media types.

  • Your DTOs can have a list of links for HATEOAS. That's the kind of thing that shouldn't be added to persistence objects. When using Spring HATEOAS, you can make your DTO classes extend RepresentationModel (formerly known as ResourceSupport) or wrap them with EntityModel (formerly known as Resource<T>).

Dealing with the boilerplate code

You won't need to map your persistence entities to DTOs and vice versa mannually. There are many mapping frameworks you can use to do it. For instance, have a look at MapStruct, which is annotation based and works as a Maven Annotation Processor. It works well in both CDI and Spring-based applications.

You also may want to consider Lombok to generate getters, setters, equals(), hashcode() and toString() methods for you.


Related: To give better names to your DTO classes, refer to this answer.

Insanity answered 23/3, 2016 at 10:16 Comment(13)
If I did go the DTO way, would you map all domain objects to a DTO or just the ones that would not be identical? Also, how would you solve the problem of exposing data based on different scenarios/contexts? Multiple DTOs per domain object?Marquardt
@Marquardt It's up to you. I usually map only the most complex entities to DTOs, the entities that I don't want to have all attributes exposed and entities with lots of relationships. DTOs give me the flexibility to have a list of links to be used in HATEOAS. That's the kind of thing I wouldn't add to my persistence objects.Insanity
@molin thanks a lot for the information and suggestions. I will definitely check out MapStruct. At a glance it looks to suit my needs very well.Marquardt
Dear downvoter, could you at least explain the reason of your downvote?Insanity
I looked through MapStruct user guide, it seems to be a very advanced tool for such cases and covers many edge cases.Crimple
There's also an architectural reason to use DTOs instead of domain entities in the REST API. The REST API should not change to avoid breaking existing clients. If you use the domain model directly in the API, you create undesirable coupling between the API and the domain model. Per the Service Loose Coupling design principle, the service contract should not be tightly coupled to the service logic or implementation details.Cortege
The real question is should we use different DTOs for different verbsHydrargyrum
@Hydrargyrum Fair question. From my experience, it depends on what sort of data the different verbs as supposed to handle. For example, if both POST and PUT operations accept the same payload, it's acceptable to define a single DTO for them. If you are looking for a meaningful name for such class, I would suggest SomeSortOfInput or even SomeSortOfResourceInput. If both operations handle different payloads, then it would make send to stick with different DTOs.Insanity
Yes, i am talking about post and put. Both verbs should have the same dto since they insert update the same resource. The problem is that proeprties in insert and update dont match, such as for example id, creationdate and other properties being assigned when persistedHydrargyrum
@Hydrargyrum Have you considered leaving those values out of the DTO? Let me clarify that: Values such as created and last modified dates are assigned by the server and shouldn't come from the request payload. For a PUT request, the id will be present in the URL, so you don't need to have it duplicated in the request payload.Insanity
Id doesnt necesseraly have to be in url. Dont forget that POST DTO could have nullable values and PUT DTO not. Also consider there is min onr more DTO for response/getHydrargyrum
@Hydrargyrum Some frameworks for validation have the concept of group, so you can define a subset of constraints in each group. It my help you, if you stick to a single class to represent the payload for both POST and PUT methods.Insanity
@Insanity What mapping framework you suggest for Node/Express? Can you please share an example of usual usage of DTO/DAO in Node?Sec
M
41

When your API is public and you have to support multiple versions, you have to go with DTOs.

On the other hand, if it's private API and you control both client and server, I tend to skip the DTOs and expose directly domain model.

Margetmargette answered 23/3, 2016 at 10:10 Comment(1)
I agree with you on the last part and I usually do so, but this is my first public API. I will consider what you say about using DTOs for the public part. Maybe the private and public parts of the API should indeed be separate, even if "eat your own dogfood" is a good principle.Marquardt
S
18

I tend to use DTOs.

I don't like the drawbacks but it seems that the other options are even worse:

Exposition of domain objects may lead to security issues and data leak. Jackson annotations may seem to solve the problem but it's too easy to make a mistake and expose data which should not to be exposed. When designing a DTO class it's much harder to make such a mistake.

On the other side the drawbacks of DTO approach can be reduced with things like object to object mapping and Lombok for less boilerplate.

Smaltite answered 23/3, 2016 at 10:11 Comment(0)
B
13

As you already stated yourself, this is clearly an opinion related question. I myself am more drawn to the No-DTOs approach, simply because of all the boilerplate code you need.

This is mainly true for the response side of a json/rest api. I even wrote a jackson addon to avoid writing many json views / filters for these cases: https://github.com/Antibrumm/jackson-antpathfilter

On the other hand DTOs are a good thing on the request input side of such APIs. Working directly on entities can be pretty hard taking into account bidirectional relations for example. Also you dont really want to let a caller modify a "creator" attribute for example. So you would need to dissallow certain fields during the mapping of such requests.

Boole answered 23/3, 2016 at 9:56 Comment(2)
I agree that my question is somewhat opinion related (and discouraged), however I was also looking for tips on how to solve my problem. I will take a lot at your Jackson addon, however do you think that using mixins to control what data should be exposed in different scenarions is a good what to go?Marquardt
I would use a mixin or @JsonView for the response body, and if really needed, a "SomethingParameters" or "SomethingPayload" for the inbound request body. I would avoid "DTO" as the plague. The word means absolutely nothing, and in real life, is hijacked by unprofessional developers as an excuse to duplicate classes both left and right nonsensically in their package-by-layer application without thinking one second about what the hell they are doing.Devon

© 2022 - 2024 — McMap. All rights reserved.