Suppose I've got a spring controller as follows:
⋮
@RestController
public class MyController {
⋮
@PostMapping(value = { "publicAPI/addItem", "internalAPI/addItem" })
public Item addItem() {
⋮
publicAPIOnlyMethod(); // Should only run in a "publicAPI" endpoint
internalAPIOnlyMethod(); // Should only run in an "internalAPI" endpoint
⋮
}
}
Furthermore, let's also say that all methods in MyController follow a similar pattern. Specifically, the pattern where we have 2 endpoints (a publicAPI and an internalAPI), and the logic between them is the same, save for the trivial publicAPIOnlyMethod() and internalAPIOnlyMethod() lines.
What I'd like is for publicAPIOnlyMethod to only run inside a publicAPI endpoint, and internalAPIOnlyMethod to run inside an internalAPI endpoint.
The options I've thought of are as follows:
- Inject
HttpServletRequestand usegetRequestURIto read which path I've got and use anifblock from there. This feels rather hacky and gives a bad smell. It side steps what appears to fundamentally be a design issue. - Split the controller into 2 controllers; one for
publicAPIand one forinternalAPI. Move all the functionality to a common service. For each method, the respective service method will now take in a flag which will decide whether to runpublicAPIOnlyMethodorinternalAPIOnlyMethod. This gets around the issue in the above (1) but now this service has to manage concerns regarding two different controllers - it breaks the single responsibility principle. - As above (2), but instead of the service passing in a flag, it passes in
publicAPIOnlyMethod/internalAPIOnlyMethodas a parameter. This gets around the issue in (2) of the service managing multiple concerns. However, the additional dynamism now contributes to lowering readability and maintainability. - Again, move all logic, but this time, to an abstract class which the two services will extend. The abstract service will require implementations to create
useAPISpecificMethodwhich it will use inside its own methods (which were imported from the controller). This approach arguably solves the issue from (3), but feels like overkill for what should be a simple solution.
I'm curious to know what other options I may not have considered here.