spring-bootrestexceptionaxonsaga

Title: Propagating Exceptions from Axon Saga to Spring Boot REST Controller


I am working on a project utilizing the Axon Framework along with Spring Boot to implement CQRS and Event Sourcing patterns. Within this project, I have defined a Saga to handle a specific business process. In this Saga, I throw a custom InvalidOrderException when certain conditions are not met. Here's a simplified version of my Saga:

@Slf4j
@NoArgsConstructor
public class OrderSaga {
    // ...
    @StartSaga
    @SagaEventHandler(associationProperty = "orderId")
    @EventHandler
    public void handle(OrderCreatedEvent orderCreatedEvent) {
        // ...
        try {
            commandGateway.sendAndWait(reserveProductCommand);
        } catch (CommandExecutionException ex) {
            rollbackReservations();
            throw new InvalidOrderException("The product ID: " + productId + " provided in the order is invalid ");
        }
        // ...
    }
    // ...
}

I want to handle this InvalidOrderException in my REST controller from where the command initiating this Saga is sent, to inform the user that something went wrong. Here's the relevant part of my REST controller:

public class OrderController {
    // ...
    @PostMapping
    public ResponseEntity<ReadOrderDto> createOrder(@RequestBody CreateOrderDto createOrderDto){
        CreateOrderCommand createOrderCommand = new CreateOrderCommand(
            // ...
        );

        commandGateway.sendAndWait(createOrderCommand);

        return new ResponseEntity<>(
            createOrderCommand.toToReadProductDto(), HttpStatus.OK
        );
    }
    // ...
}

To handle the InvalidOrderException, I tried to create a GlobalExceptionHandler class annotated with @RestControllerAdvice, like so:

public class GlobalExceptionHandler {
    @ExceptionHandler(InvalidOrderException.class)
    public ResponseEntity<String> handleProductServiceException(InvalidOrderException exception) {
        return new ResponseEntity<>(exception.getMessage(), HttpStatus.BAD_REQUEST);
    }
}

However, this GlobalExceptionHandler does not catch the exception thrown from the Saga. I am looking for a way to propagate this exception back to the REST controller to send a meaningful error response to the client.

I also used the @ExceptionHandler annotation of Axon but this will only work if the exception is thrown in the same class, so this did not help.

Is there a recommended way to handle such exceptions thrown from an Axon Saga in a Spring Boot REST controller, especially to capture and respond to these exceptions where the command was initially sent?

Thank you.


Solution

  • I'm not sure returning something from the Saga to the rest controller is the right approach. As a Saga is used for a potentially long async running process, from the initial rest call I would just expect a failure, or an Ok, order started kind of return.

    From the Saga you could send the appropriate commands or events, so that a projection can be created from which the status can be retrieved. So for example, you could start a subscription query on the order, to provide feedback in an ui.