Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring rest endpoints and service layer separation

Tags:

rest

spring

In spring boot application, I have two services: GroupService and UserService.There is also respective models classes, Group and User as well as the repositories. I am exposing two Rest endpoints, one for user and one for group. Now, I have an endpoint which adds the given user to a given group something like below, forget about the possible null checks and other issues at the moment.

@PostMapping(path="groups/{group-id}/add/{user-id}")
public ResponseEntity<Group> setUserGroup(@PathVariable(value="group-id")Long groupId, @PathVariable(value="user-id")Long userId)
    {   Group group=groupService.findById(groupId);
        group.addUser(userService.findById(userId));
        return new ResponseEntity<Group>(groupService.save(group),HttpStatus.OK);
    }

Now, the question is, what is the best practice to handle a scenario like that. Is not it good to handle the logic in Group service by injecting user service on it, rather than handling it in rest endpoint? In that case, the group service will be dependent on user service, but at least the logic can be reused if (lets imagine such a case) multiple rest endpoints require it.

The current implementation has an advantage as the services does not become dependent on each other. But, if we have complex logic, and lets say we have complex transactions, then definitely rest endpoint is not the right place for handling it. Please help me to understand which approach I should use, and what is the industry best practice.

like image 683
Imran Avatar asked Oct 26 '25 03:10

Imran


1 Answers

It seems we have typical 3-tier architecture here!

  • The proper place for endpoints is Controller.
  • Business logic like adding user to group should be located in service layer. You free to create services and inject one inside another, but avoid circular dependencies. Also DAOs should be injected into services.
  • Transaction management should be implemented on service layer. (See Spring's @Transactional annotation)
  • If your classes "GroupService" and "UserServise" contains methods for direct storage manipulation(e.g create/read/update/delete/find by/etc.), they should not logically belong to service layer. Such classes belong to persistence layer and should be named like GroupRepository or GroupDao (DAO - Data Access Object).

  • Repository may be implemented manually, but popular way is to use Spring Data interfaces. I strongly recommend to check http://projects.spring.io/spring-data/#quick-start

In your case I see the following picture:

@Component
GroupRepository {
   // Manually implemented DAO, but more simple way is Spring Data
   ...
}

@Component
UserRepository {
   // Manually implemented DAO
   ...
}

@Service
ManagementService {
  @Autowire
  private UserRepositoty userRepository;

  @Autowire
  private GroupRepository groupRepository;

  @Transactional
  public Group addUserToGroup (Long groupId, Long userId) {
         Group group=groupRepository.findById(groupId); group.addUser(userRepository.findById(userId));
          return groupRepository.save(group);
    }
}

@Controller
ManagementController {

      @Autowire
      private ManagementService managementService;

      @PostMapping(...)  
      public ResponseEntity<Group> setUserGroup(@PathVariable Long groupId, @PathVariable Long userId) { 

          return new ResponseEntity<Group>(managementService.addUserToGroup(groupId, userId),HttpStatus.OK);
      }
}

See https://www.petrikainulainen.net/software-development/design/understanding-spring-web-application-architecture-the-classic-way/

P.S. One remark regard usage of ResponseEntity: it is not mandatory in most cases, the simplified code below. Magic is behind RestController annotation. https://spring.io/guides/gs/rest-service/

@RestController
ManagementController {

      @Autowire
      private ManagementService managementService;

      @PostMapping(...)  
      public Group setUserGroup(@PathVariable Long groupId, @PathVariable Long userId) { 

          return managementService.addUserToGroup(groupId, userId);
      }
}
like image 162
Yurii Bratchuk Avatar answered Oct 29 '25 05:10

Yurii Bratchuk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!