0

Good day, I have a Spring Boot based backend , we are using own library to convert JPA entities to Dto's (library works based on reflection). The problem is , we inject service layer directly to some mappers. Let's say I have a UserEntity and UserDto. UserDto has a field called avatar and avatars are stored in S3. So in order to build a UserDto we are using the code like this.

@Component
class UserMapper {

 @Inject
 S3Service s3Service;

 public UserDto toDto(UserEntity entity){
     UserDto dto = new UserDto();
     BeanUtils.copy(entity,dto);
     dto.setAvatar(s3Service.getAvatarByUser(entity));

}

}

I don't like this approach because Mapper mustn't know anything about Service layer . However this mapper is used by other mappers as well. In case I want to return an OrderDto, it has a nested UserDto so OrderDto calls UserMapper internally. Are there any best practices for Mappers to be service free ?

So far I tried the following.

  1. Store avatar in ThreadLocal cache. When controller calls a service to get a user, service will store user's avatar in the ThreadLocal, and then Mapper will get it from ThreadLocal cache. Disadvantage - it's hard to test it and requires me to make Mocks
  2. Create a separate POJO called UserWithAvatar that stores UserEntity entity;String avatar and create a mapper for UserWithAvatar instead of UserEntity. Disadvantage - as I said this mapper will be used by OrderMapper and order mapper takes OrderEntity with nested UserEntity instead of UserWithAvatar
Almas Abdrazak
  • 3,209
  • 5
  • 36
  • 80
  • i dnt understand why are you injecting services to mapper and not the other way around? why dont u inject mapper to the service? that makes more sense to me and fulfills ur wish – J Asgarov Apr 30 '21 at 14:39
  • our service layer returns JPA entities and it's up to controller layer to know how to convert entities into Dto's that is why we inject mappers to controllers – Almas Abdrazak Apr 30 '21 at 14:41
  • You can't always be entirely theoretically pure. Software design is about tradeoffs. – chrylis -cautiouslyoptimistic- Apr 30 '21 at 14:59
  • @chrylis-cautiouslyoptimistic- agree, but this problem is not unique I suppose so I was wandering if some pattern exists for this case – Almas Abdrazak Apr 30 '21 at 15:01

1 Answers1

0

I think mapper should be inside of service but I will try working with ur requirements

u have 2 choices:

  1. You inject both service and mapper to controller, get entity back to the controller and map it using mapper before returning response

  2. Use event publishing to publish an event which mapper then catches and produces the mapping. After that you could either directly return the dto to controller or produce another event. (event publishing is by default synchroneous so you dont have to worry about concurrency issues)

Event publishing is done via spring and results in very uncoupled code where publisher doesnt know anything about event subscribers and hence these 2 can be in 2 seperate layers that wont know anything about each other

Easy to follow guide: https://www.baeldung.com/spring-events

J Asgarov
  • 2,526
  • 1
  • 8
  • 18
  • 1. It won't work, other mappers depend on UserMapper, and they assume that UserMapper knows how to set avatar field (it's something like MapStruct library, mappers call each other in case they have a nested dtos) 2. Not sure about this , I know about spring events but don't think they are suitable here . – Almas Abdrazak Apr 30 '21 at 14:55
  • I have successfully implemented Spring events to communciate between layers that had no knowledge of each other (seperated maven modules) so it does work - but of course do take a look if you come up with something else. – J Asgarov Apr 30 '21 at 15:59