简体   繁体   中英

Data Transfer Object DTO Where to build

Trying to refactor some code. I see some classes creating DTO objects which are being passes in the service layer and returned later by the @RestController. As I know it is best to build the Data Transfer Objects only in the controllers and pass them to the views, especially when we use something like WrapperDTO<T> with get and set value. Maybe there are differences when we are building the WrapperDTO with complex object or simple data types. All oppinions will be well appreciated.

DTO may be used to transfert data between the different layers of an application: DAO, Service, Facade, Controller. In my experience DTO is an opinionated topic.

In my opinion, the later the conversion, the better, it is even better if no conversion is needed. Generally, the later is at the application boundary. DTO is not free, it involves mapping and its support. Hence DTOs will make sense when there is a domain model mismatch or a model technical mismatch accross the boundary. For more information you can have a look at LocalDTO article and the associated link .

If I focus on the service -> facade -> controller layers:

  • Services: They are doing services things and they may call each other to do their processing. If your domain models remain consistent across the services boundary service => facade it is too early to convert the result into a DTO.

  • Facades: They may orchestrate services and convert input/output. In my point of view it will be the right place to convert to or from DTO. But only if it is needed to ie. because your domain models have to be transformed accross this boundary (filtering fields, aggregation...)

  • Gateway/Controllers: They are at the application boundary. Their logics are simple, reduced to the boundary logic. The relation between a facade and a controller is usually one <-> one . ***

    Merging facades and controllers usually make sense


Thus in my point of view, your first proposal is more adapted eg. UserController.... . The most important is to stay pragmatic.

I would say it's better to create DTO on Service layer.

Controller must not know business logic details. Eg we need to return User info but some fields (password etc.) must be excluded. The fields exist in the User entity but must be removed from DTO.

Another case we got SomePaginationDTO on Controller and we still need to pass the DTO to service to parse filter, apply sort, limit results etc. All the logic is part of service responsibility. So I would pass the SomePaginationDTO to service.

I would say there is no canonically right way to do this. It depends on what is DTO used for.

I found a simple rule for myself on where to use DTO in service level and where to not: In case if service level has only one client I use DTO across service level. In case if there are two or more clients it is better not to use DTO in service level.

Let me explain this in more details:

Basically it is clear that not including DTO into service level requires more efforts. So in case if there is only one client I would keep things simple and use DTO as a return type for service methods.

In case if there are more then one client of the service most likely the clients will require different DTO (for instance I want to have json and csv representation of an object). In such case I will not return DTO from service. Otherwise I would need to have different services for each DTO, or different service methods, etc.

Note: I'm not saying that if you don't use DTO in service level you must move the transformation logic into controller level. I still think that controller level must be as simple as possible. You might have some intermediate transformation level between controller and service - uncomfortable, but it is much better then having several services.

I found a great answer as a part of other question. Of course this/my answer does not participate for the bounty. It is just an inspiration someone to explain how the things are implemented nowadays. So take a look:

  • Model classes. They don't get getters/setters for everything. It depends on model logic. Otherwise you get model where you can easily break consistency. Or where are many unobvious things. Suppose you have User.registrationDate field. When you construct a new User object, you should not forget to field registrationDate field by hands. So, just place registrationDate initialization in your constructor and remove setter!
  • Repository interface just inside your model. Suppose you have some business logic that depends on existing stored objects. You can't expliciltly refer from your domain logic into infrastructure dependencies like JPA, Hibernate, JDBC, etc. So you query this stored objects from interfaces.
  • Business services (optionally). They implement some complex logic, involving many different entities, not including security and transaction management. Your question is about it. Yes, if you need query for entities in your domain logic, place query into repository and call it from your business service.
  • Repository implementation inside infrastructure package. Implements repository interfaces using JPA or mockito or whatever else. They also don't include neither security nor transactions.
  • Application services (optionally). If there is some complex interaction with infrastructure or security checking.
  • Remote facade interface. Client and server communicate only through remote facade interface.
  • Remote facade implementation (controller). Transforms thick entity objects into thin DTOs (data transfer objects). All transaction demarcation and security is here (often using annotations).

What do you think about that design? Is it applicable for the well know spring mvc?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM