Microservices Restful API - DTOs or not?

12,308

Solution 1

The Pros of Just exposing Domain Objects

  1. The less code you write, the less bugs you produce.
    • despite of having extensive (arguable) test cases in our code base, I have came across bugs due to missed/wrong copying of fields from domain to DTO or viceversa.
  2. Maintainability - Less boiler plate code.
    • If I have to add a new attribute, I don't have to add in Domain, DTO, Mapper and the testcases, of course. Don't tell me that this can be achieved using a reflection beanCopy utils, it defeats the whole purpose.
    • Lombok, Groovy, Kotlin I know, but it will save me only getter setter headache.
  3. DRY
  4. Performance
    • I know this falls under the category of "premature performance optimization is the root of all evil". But still this will save some CPU cycles for not having to create (and later garbage collect) one more Object (at the very least) per request

Cons

  1. DTOs will give you more flexibility in the long run
    • If only I ever need that flexibility. At least, whatever I came across so far are CRUD operations over http which I can manage using couple of @JsonIgnores. Or if there is one or two fields that needs a transformation which cannot be done using Jackson Annotation, As I said earlier, I can write custom logic to handle just that.
  2. Domain Objects getting bloated with Annotations.
    • This is a valid concern. If I use JPA or MyBatis as my persistent framework, domain object might have those annotations, then there will be Jackson annotations too. In my case, this is not much applicable though, I am using Spring boot and I can get away by using application-wide properties like mybatis.configuration.map-underscore-to-camel-case: true , spring.jackson.property-naming-strategy: SNAKE_CASE

Short story, at least in my case, cons doesn't outweigh the pros, so it doesn't make any sense to repeat myself by having a new POJO as DTO. Less code, less chances of bugs. So, going ahead with exposing the Domain object and not having a separate "view" object.

Disclaimer: This may or may not be applicable in your use case. This observation is per my usecase (basically a CRUD api having 15ish endpoints)

Solution 2

I would vote for using DTOs and here is why:

  • Different requests (events) and your DB entities. Often it happens that your requests/responses different from what you have in the domain model. Especially it makes sense in microservice architecture, where you have a lot of events coming from other microservices. For instance, you have Order entity, but the event you get from another microservice is OrderItemAdded. Even if half of the events (or requests) are the same as entities it still does make sense to have a DTOs for all of them in order to avoid a mess.
  • Coupling between DB schema and API you expose. When using entities you basically expose how you model your DB in a particular microservice. In MySQL you probably would want to have your entities to have relations, they will be pretty massive in terms of composition. In other types of DBs, you would have flat entities without lots of inner objects. This means that if you use entities to expose your API and want to change your DB from let's say MySQL to Cassandra - you'll need to change your API as well which is obviously a bad thing to have.
  • Consumer Driven Contracts. Probably this is related to the previous bullet, but DTOs makes it easier to make sure that communication between microservices is not broken whilst their evolution. Because contracts and DB are not coupled this is just easier to test.
  • Aggregation. Sometimes you need to return more than you have in one single DB entity. In this case, your DTO will be just an aggregator.
  • Performance. Microservices implies a lot of data transferring over the network, which may cost you issues with performance. If clients of your microservice need less data than you store in DB - you should provide them less data. Again - just make a DTO and your network load will be decreased.
  • Forget about LazyInitializationException. DTOs doesn't have any lazy loading and proxying as opposed to domain entities managed by your ORM.
  • DTO layer is not that hard to support with right tools. Usually, there is a problem when mapping entities to DTOs and backwards - you need to set right fields manually each time you want to make a conversion. It's easy to forget about setting the mapping when adding new fields to the entity and to the DTO, but fortunately, there are a lot of tools that can do this task for you. For instance, we used to have MapStruct on our project - it can generate conversion for you automatically and in compile time.

Solution 3

The decision is a much simpler one in case you use CQRS because:

  • for the write side you use Commands that are already DTOs; Aggregates - the rich behavior objects in your domain layer - are not exposed/queried so there is no problem there.
  • for the read side, because you use a thin layer, the objects fetched from the persistence should be already DTOs. There should be no mapping problem because you can have a readmodel for every use case. In worst case you can use something like GraphQL to select only the fields you need.

If you do not split the read from write then the decision is harder because there are tradeoffs in both solutions.

Share:
12,308

Related videos on Youtube

so-random-dude
Author by

so-random-dude

Thanks for the profile visit. If you find my answers helpful I will definitely appreciate an "upvote". If you find things to be improved, comments are most welcome, I would definitely appreciate that too. Oracle Certified Java Associate Google Certified Cloud Architect AWS Certified Solution Architect

Updated on June 07, 2022

Comments

  • so-random-dude
    so-random-dude almost 2 years

    REST API - DTOs or not?

    I would like to re-ask this question in Microservices' context. Here is the quote from original question.

    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.

    Now, My question

    I am more aligned towards using one Object through all the layers of my application (In other words, just expose Domain Object rather than creating DTO and manually copying over each fields). And the differences in my Rest contract vs domain object can be addressed using Jackson annotations like @JsonIgnore or @JsonProperty(access = Access.WRITE_ONLY) or @JsonView etc). Or if there is one or two fields that needs a transformation which cannot be done using Jackson Annotation, then I will write custom logic to handle just that (Trust me, I haven't come across this scenario not even once in my 5+ years long journey in Rest services)

    I would like to know if I am missing any real bad effects for not copying the Domain to DTO

    • fps
      fps almost 7 years
      As with the linked question, this is totally opinion-based (though it is a very good question, indeed)
    • so-random-dude
      so-random-dude almost 7 years
      @FedericoPeraltaSchaffner Thanks for the comments. I am looking for more data points/facts, not an opinion :) If I cannot get an answer to this question from Stackoverflow platform, I may not get it from anywhere else. IMHO, Stackoverflow should reconsider what should be categorized as opinion.. Meanwhile I understand that if I ask say, "Gradle Vs Maven, which one should I choose", its an opinion seeking question.
    • fps
      fps almost 7 years
      Yes, I agree with you, the problem is that it's quite difficult to draw that line...
    • Stanislav Bashkyrtsev
      Stanislav Bashkyrtsev almost 7 years
      So tiring that many questions are closed because they are opinion-based. I like opinions and often they are more valuable to me than the facts (which I can easily find in sources/docs of the target lib/framework/etc). The guy just needs help..
    • Allen Shi
      Allen Shi over 6 years
      Personally, I would vote for Danylo Zatorsky's answer. DTO is really client-driven and designed by contract. In some pattern of micro-services, like aggregation, DTO would play an important role across different micro-services.
  • Nikolaj Dam Larsen
    Nikolaj Dam Larsen about 6 years
    For CRUD services with an anemic "domain model", I completely agree with you. With a rich complex domain model, it's a bit more difficult. So in my opinion it's circumstantial and it will, in a microservice setting, probably differ from service to service, context to context.