38

We are creating rest api's with Spring Boot. We have three layers in our project(Repository, Service and Controller).

Lets say I have GetUser api in my controller that return UserDTO object.

@GetMapping
public UserDTO getUser() {
   return userService.getUser();    
}

Whether userService.getUser() returns UserDTO object or it returns User object and it is converted to UserDTO object in the controller? Which one is better way?

Shortly, domain object to DTO object conversion, should be done in service layer or controller layer?

deamon
  • 89,107
  • 111
  • 320
  • 448
Suleyman Arıkan
  • 503
  • 1
  • 4
  • 7
  • its best practice to convert the DTO in the **service layer**, don't put any logic in the **controllers**. – Ahmed Nabil May 29 '20 at 21:47
  • top down. controller should know service, service should know repository. Service layer should not know controller, endpoint dto's. – alex Feb 02 '22 at 19:32
  • @dieter Could you elaborate? So the service should return `User` or `UserDTO`? – parsecer Jun 02 '23 at 20:54
  • @parsecer today I would return from service (core application) 'CoreUser', controller should do mapping 'CoreUser' to 'ApiUser' and return it. So service layer has no idea about Controller and rest api. – alex Jun 03 '23 at 05:40

4 Answers4

23

I think there is no "better way" for converting your domain objects to your DTO objects, it's a matter of taste. In my projects I convert the domain objects to the DTO in the service layer as part of my "business logic". So you reduce the accessability of your domain objects only to your service layer. Furthermore I want to reduce the "logic" inside my controllers as they are part of the application layer.

PS: If you are looking for several ways to convert your domain objects to your DTOs have look at one of my latest Stackoverflow questions (How to properly convert domain entities to DTOs while considering scalability & testability)

rieckpil
  • 10,470
  • 3
  • 32
  • 56
  • 1
    If I want to support partial entity update from JSON (PATCH, supply only what requires changing), I currently do it with jakson objectMapper.readerForUpdating(...). But to update the resource, I need to update the DTO (readerForUpdating(find(id).toDTO()) and then save the dto.toEntity(). That means that now I need to call jackson (third party lib) from my services. Doesn't that bother you? – Dalibor Filus Mar 27 '22 at 10:57
  • On the other hand, if the conversion is done in the controller, I would have to support this partial update in the controller itself, which means making all of my "domain" object public (assuming the controllers live in other module than the services do). – Dalibor Filus Mar 27 '22 at 10:58
  • Also, there is a relevant issue: if I do need to also support xlsx/csv export of the data, the xlsx/csv should contain the same data as the DTO does. Where would you put that logic? To the service, controller, or some secondary service used just to convert this DTO to xlsx/csv? You could convert domain entity to xlsx/csv directly, ofc, but then you have access to the domain in the same place where you access third party xlsx library (apache.poi for example). – Dalibor Filus Mar 27 '22 at 11:18
  • 1
    If the service layer returns entities i guess we have another problem regarding transactions. If the entity returned by the serivce has some lazy collection ,we should open the transaction in the Controller to acces the data right? Is ok to open the transaction from the controller? – Al Xx Jun 14 '22 at 13:06
12

It depends on application needs and architecture. Idea is to keep dto conversion at edge. It is generally prefer to have dto and domain conversion at the controller level. If you want to keep services/business logic independent of consumer, then it is always better to have at api level. This becomes more clear if your service has been consumed by more than one consumer.

Imran Javed
  • 11,779
  • 2
  • 16
  • 17
5

In my experience, the conversion should be on the Controller layer. This gives an advantage that able to reuse other service methods with the return object is the origin.

This point may be important sometimes because the DTO object often reduce fields from the origin object. Therefore, we need more code to get these reduced fields, making our code ugly and duplicated.

I know that it would be moving logic to the controller layer, but it is a tradeoff.

NickNgn
  • 122
  • 2
  • 8
1

Here is what I do in general:

My service layer takes dto as an argument. I do the conversion at the service layer, because I might need to apply some business logic when converting.

But my service layer always returns entity. And I do the conversion at the controller level. This makes my service is clean and independent of the consumer. It may happen that my service might be consumed by another service and need entity not the dto.

In summary:

Dto —> Entity (Service layer)
Entity —> Dto (Controller layer)

hq-coder
  • 194
  • 1
  • 8