Reputation: 763
I'm just thinking, what is the best practice to create PATH mapping for rest service. Let's say we have following paths:
/users POST
/users/1 PATCH, GET
/users/1/contacts GET, POST
/users/1/contacts/1 GET, PATCH
The question is - what is the best practice to create controllers. For example we have UserController where we technically could put all these mappings. Or - we should create seperate controllers (UserController, ContactsController). f.e UserController below, if we put everything under.
@RequestMapping("users")
@RestController
public class UserController {
@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Void> createUser() {}
@RequestMapping(method = RequestMethod.GET)
public User getUser() {}
@RequestMapping(value = "{id}/contacts", method = RequestMethod.GET)
public List<Contact> getContacts() {}
@RequestMapping(value = "{id}/contacts", method = RequestMethod.POST)
public ResponseEntity<Void> createContact() {}
.....
}
And if we create separate controllers, how paths should be organized then? Probably it's a silly question, but i will be glad, if someone could share experience.
Upvotes: 12
Views: 26847
Reputation: 14031
I am a fan for separate controllers as they remove coupling between Users
and Contacts
(for example) - what if later you want to use Contacts
in a separate context? (i.e. independent of the User
they belong to)
If they are separated, the paths would look very similar to what you have:
Users
/users GET, POST
/users/{user-id} PATCH, GET
Contacts
/contacts GET, POST
/contacts/{contact-id} GET, PATCH
If there is a dependency between Contacts
and Users
(and in this case, it looks like there is one), you can have it like this:
/contacts/for-user/{user-id} GET, POST
/contacts/for-user/{user-id}/{contact-id} GET, PATCH
This allows you to add contacts to other things (not just users) and users to other contexts (regardless of their contacts)
For example, Users of a coffee maker
/coffee-maker/users GET
Or if the coffee maker fails, who do we contact for repair?
/coffee-maker/contacts GET
Probably a contact for a coffee maker repair is also a User (but they don't have to be)
One more thing, you can turn it around and say that a contact is for an appliance (i.e. a coffee maker)
/contact/for-appliance/{appliance-id} GET
My point being that if you decouple the contacts from the users, you can assign contacts to other entities instead of forcing the relationship to be User -> Contacts
- unless, of course, you don't want to decouple them because of business rules or some reason
In any case, beware that there are many ways of doing the mapping
Upvotes: 1
Reputation: 4381
Lets suggest that number of entities related to User will increase in future. So it obvious that it is better to split it according to entities:
UserController -> UserService -> UserRepository,
ContactController -> ContactService -> ContactRepository,
FriendshipController -> FriendshipService -> FriendshipRepository
From my experience, User Controller
@RestController
@RequestMapping("/user")
public class UserController extends AbstractController {
...
@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> createUser(@RequestHeader("X-Auth-Token") Optional<String> @RequestBody User user) {
...
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<?> listUsers(@RequestHeader("X-Auth-Token") Optional<String> authToken) {
...
related to user scope Friendship controller:
@RestController
@RequestMapping("/user/{id}")
public class FriendshipController extends AbstractController {
...
@RequestMapping(value = "/friendship/code", method = RequestMethod.POST)
public ResponseEntity<?> generateCodeForUser(@PathVariable("id") long id) {
...
@RequestMapping(value = "/friendship/code", method = RequestMethod.GET)
public ResponseEntity<?> retrieveCodeForUser(@PathVariable("id") long id) {
...
Not sure it is axiom, but help me organize my code.
Upvotes: 13