Which is best layer to place mapper code, service layer or controller layer?
Asked Answered
R

5

14

I want to ask about Architectural pattern. I write two snippet code to demo what I ask.

The first way is:

//a method on controller layer (in Spring framework)
@RequestMapping(...)
public ShopDTO findShop(final Long shopId) {
    Shop shop = shopService.getShopById(shopId);
    ShopDTO shopDTO = shopMapper.toShopDTO(shop);
    return shopDTO;
}

//A method on service layer
@Transactional
public Shop getShopById(final Long shopId) {
    //some code to find an entity by id
}
  • Note: the code what maps from shop entity to ShopDTO in controller layer.

The second way is:

//a method on controller layer (in Spring framework)
@RequestMapping(...)
public ShopDTO findShop(final Long shopId) {
    ShopDTO shopDTO = shopService.getShopById(shopId);
    return shopDTO;
}

//A method on service layer
@Transactional
public ShopDTO getShopById(final Long shopId) {
    Shop shop = shopRepository.findById(shopId);
    ShopDTO shopDTO = shopMapper.toShopDTO(shop);
    return shopDTO;
}
  • Note: the code what maps from shop entity to ShopDTO in service layer.

I use Spring framework code for example.

My question is: Which is the best layer to place mapper code. And can you tell me why?

By the way, what type logic should place on controller layer and what should place on service layer?.

Roshelle answered 23/11, 2017 at 13:44 Comment(4)
i don't know how spring work, but i think having a complete dll doing this part is a good design. There you can do the translation between the two objects, you can even use implement some translation businessRule there. thus you could reuse this dll for other componentsSwizzle
Thanks for your answer, I have other component for mapping. I know it the best. What i want to ask is: what type logic should place on controller layer and what should place on service layer. I have updated my question.Roshelle
controller shoud do nothing. only offering contextual entries, functions meaning things to you business. In these functions you shoud then call your service layer with basic data functions (update, delete, create). all the logic should be in those service functions. Ideally if your controller could have no "if logic" that would be the bestSwizzle
Im just working right now in a MVC project and as I can see, in cases like yours, the best choice is to work on the service layer.Autotype
S
10

The second way is better.

The reason is, that you want to have certain abstraction between your layers. The controller should just get the shop by its id. That way, you can change how you map and retrieve data without having to change the controller. Then you should go to the next step and think how to best design the abstraction on the boundaries of your data access layer and the service layer.

About what logic to have in the controller. In the controller you should place logic related to the incoming request and the final response of your API. Maybe you can do some validation (althought most is better to be performed via interceptors), extract header values and do things with them and have your final exception handling clause so that nothing spills outside your API unexpectedly and you can reply with the proper HTTP 5xx response. In general the controller should not have large methods, they are primary there to expose your API endpoints.

Sammer answered 11/12, 2017 at 10:58 Comment(0)
W
4

For a years of development on Spring Rest API I thought that the second approach is better.

I finally adopted the first approach for the following reasons:

1-Mapping the entity to the DTO is related to your API

You can use the same service function

@Transactional
public Shop getShopById(final Long shopId){
    //some code to find an entity by id
}

but in one API you need only two fields, on the second one you need all fields, so in this case you'll have two controllers methods but only one service method.

2-Other Classes that call directly your service

In most of my last experiences, I had a batch that calls directly the service layer, in case you return a DTO in service layer, you'll have to map the DTO to Entity again or rewrite the same method without mapping.

3-Based on SOLID principles S stands for single responsability

It means that a class or a method should do only one thing, hence when you do this

@Transactional
public ShopDTO getShopById(final Long shopId){
    Shop shop = shopRepository.findById(shopId);
    ShopDTO shopDTO = shopMapper.toShopDTO(shop);
    return shopDTO;
}

You are actually retreiving data from database and transforming the data on the same time.

Waistline answered 2/6, 2023 at 10:18 Comment(2)
I have to disagree with you. What about JPA lazy loading? What if you need to return more than Shop data? Or less than the whole entity and you could use projection otherwise? Please see my answer for details.Giacinta
Services handling DTO conversions are a symptom of an anemic data model. DTOs should be created on the external borders of our system. Internal Services should not handle DTO conversion, as this is not part of their responsibility, and it restricts other internal services from working with domain objects. @Giacinta if you need to return more than shop data, you probably need another domain object/ or an aggregate, and possibly another service for the specific domain logic.Grazier
G
2

To help answer your question here are some challenges:

1. How are you going to deal with JPA lazy joins inside the model entity? Do you need to load them before returning from the service or not? Don't forget about @Transaction scope

2. What if your API has to return more data than the Shop contains? Like data from another table/service?

3. What about request body model? Would you pass the received validated DTO straight into service or map it into something else? Why?

Suggestions:

  1. Do mapping from/into DTO on the service layer, because:
    • You know what exactly data is expected and should be loaded. Also you can use projection (in case of MongoDB).
    • You can process request DTO inside your service by loading all required data and building correct model entity (and not receiving invalid not full version of it from the controller).
  2. Use controller layer only for HTTP, MVC, gRPC etc. specific stuff like populating ViewModel, mapping response codes, reading headers etc.
  3. You can always have multiple methods that return different representation of your entity and reuse Shop getShopById.
  4. In case of more complex mapping you can create another layer like Facade (but consider it carefully since you don't want to have just a proxy layer).
Giacinta answered 10/10, 2023 at 13:35 Comment(0)
D
0

You could also just create a Mapper class with a static method

Example:

@Data // lombok for simplification
@Builder
public class User {
  private String id;
  private String name;
  private String superPrivateData;
}

@Data
@Builder
public class UserDto {
  private String id;
  private String name;
}

public class UserMapper {
  public static UserDto toDto(User user) {
    return UserDto.builder().id(user.getId()).name(user.getName()).build();
  }
}
Doldrums answered 17/7 at 10:17 Comment(0)
R
0

I'm a C#.NET developer. I'm using auto mapper in the service layer.

1. Introduction to AutoMapper

AutoMapper is a library that helps in mapping objects, typically between DTOs (Data Transfer Objects) and domain models. It simplifies object-to-object mapping in applications, reducing the need for manual conversion code.

2. Using AutoMapper in Controllers

  • Pros:
    • Simplicity: Directly using AutoMapper in controllers can reduce boilerplate code.
    • Rapid Development: Quick setup and implementation, making it easier for small-scale applications.
  • Cons:
    • Scalability Issues: As the application grows, maintaining mappings in controllers can become cumbersome.
    • Single Responsibility Principle Violation: Controllers should focus on handling HTTP requests and responses, not on mapping logic.
    • Testability: Testing controllers with mapping logic embedded can be more challenging.

3. Using AutoMapper in the Service Layer

  • Pros:
    • Separation of Concerns: Keeps the controllers clean and focused on their primary role.
    • Maintainability: Easier to manage and update mapping logic as the application scales.
    • Testability: Easier to write unit tests for services that handle mapping.
    • Reusability: Mapping logic in the service layer can be reused across different controllers.
  • Cons:
    • Initial Complexity: Setting up services with AutoMapper may require more initial setup and configuration.
    • Learning Curve: Developers need to understand the service layer architecture.

4. Best Practices

  • Encapsulate Mapping Logic: Always aim to encapsulate mapping logic outside of controllers. Use the service layer for better separation of concerns.
  • Profiles: Utilize AutoMapper profiles to organize and manage mappings.
  • Dependency Injection: Inject IMapper into services to facilitate testing and maintainability.
  • Configuration Management: Keep AutoMapper configuration centralized to avoid redundancy.

5. Conclusion

Using AutoMapper in the service layer is generally the better practice due to improved maintainability, testability, and adherence to design principles like the separation of concerns. While using it directly in controllers can work for small applications, it doesn't scale well for larger, more complex systems.

By following these best practices, you can ensure that your MVC application remains clean, maintainable, and scalable as it grows.

Rudie answered 4/8 at 9:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.