Some days ago I was discusing with my colleage Juan Antonio Breña how you could end breaking your control flow when handling errors in a Microservice, specially relaying in mechanism such ControllerAdvice
and ExceptionHandler
when create Spring Boot Microservices in Java.
I told Juan that we may need to handle this probllem differently, so I decide to create a new project in our Learning By Example GitHub meta group: Microservices Error Handling
The idea behind that project is to check how we could avoid to use ControllerAdvice
and ExceptionHandler
, and even checked exceptions when handling errors in our Microservices.
Checked exceptions some times could leak the implementation details of our components, and they could even break our control flow.
Spring ControllerAdvice
or ExceptionHandler
could become the ultimate GOTOs, that some people my use to get errors and sent back to the consumers of the Microservice, jumping out on the execution of our business logic.
The approach described in the example is to handle errors as part of our business logic, avoiding unnecessary lost of our control flow.
This example is not about stop using ControllerAdvice
, we may need to still using it to handle unexpected Runtime Exceptions, however relaying on it for handling our business errors isn’t ideal.
Approach like the one in this example may become a better way to do it, however this is not the unique, neither definitive, solution.
We have created one wrapper class to support this concept named Result<type>
, this class has two creation methods.
Result.create(object)
: That will create a wrapper containing the data to be returned.Result.error(object)
: That will create a wrapper containing an error.
How to use this class is simple, for example:
BadParameters
and NotFound
are entities for our business logic that contain the desired information for handling these responses.
For handling the result we could just use the methods isError()
and getData()
.
Finally we use in our mapping a generic ResponseEntity we could return the wrapper value without needing to understand what contains.
For example:
Since we may like to return different HTTP status, based on the error contained, we could create a helper that use the type of the class held on the wrapper.
To just run the example microservice you could just do from the command line:
To test the output when sending a correct customer id:
To test the output when sending a invalid customer id
To test the output when sending a not found customer id
The full example could be found under this repository:
https://github.com/LearningByExample/MicroservicesErrorHandling
I don’t know if we definetly will be using this approach however I think I may rewrite it in Kotlin in somepoint.