how can I handle exceptions coming from the gateway implementation when we are building software using the Onion Architecture?
To clarify, I've created an example using Java and SpringBoot:
@Component
@AllArgsConstructor
public class SaveAddressUseCase{
private final GetCustomerGateway getCustomerGateway;
public void execute(AddressDomain address, Long customerId){
try{
//validates the customerId supplied and returns the customer from an external service.
CustomerDomain customer = getCustomerGateway.execute(customerId);
address.setCustomer(customer);
//saves the address
}catch(CustomerNotFoundException ex) {
AddressErrorDomain addressErrorDomain = new AddressErrorDomain();
//saves the address in a error table with httpStatus 404
} catch (InternalErrorException ex) {
AddressErrorDomain addressErrorDomain = new AddressErrorDomain();
//save the address in a error table with httpStatus 500
}
}
}
This is a simple useCase that will save an address but first, it needs to get the customer of this address from an external service. If the customer is not found, I need to save the address in an error table to processes it later. The same goes if this external service is down, but it's important to differentiate between these two errors and I can handle this problem using the HttpStatus returned from my API call.
public interface GetCustomerGateway {
CustomerDomain execute(Long customerId);
}
@Component
@AllArgsConstructor
public class GetCustomerGatewayImpl implements GetCustomerGateway {
private final CustomerApi customerApi; //Feign interface to call an external service
public CustomerDomain execute(Long customerId){
try{
return customerApi.getCustomerById(customerId);
}catch(FeignException ex){
if (ex.status() == HttpStatus.NOT_FOUND.value()) {
throw new CustomerNotFoundException();
} else {
throw new InternalErrorException();
}
}
}
}
Lastly, this is my gateway implementation that just makes a call to this external service using a simple Feign interface and throws two custom exceptions that I extended from RuntimeException.
Question: Catching these two exceptions in the usecase I'm not dealing with details that only the gateway must know? Or even worse, I'm not using exceptions to control the flow of my application? How can I handle the errors coming from the Gateway implementation in a better way than I did in my example?
Obs: In this example, it's important to save the address in error table to not ruins the user experience in the client-side, and I also need to differentiate between these errors.
Thanks in advance!
Consider using @ControllerAdvice for this to keep the controller clean and focused
@ControllerAdvice
@Slf4j
public class RestExceptionHandler {
//Magic happens here
}
Inside RestExceptionHandler, you can catch all feign exceptions like this and handle them however you want
@ResponseBody
@ExceptionHandler(Throwable.class)
public final ResponseEntity<?> handleFeignExceptions(Exception ex, WebRequest request) {
if (ex instanceof FeignException) {
return handle((FeignException) ex);// define your custom handle method
}
}