I am working on a small event-sourced proof-of-concept using Spring Boot and Axon Framework to compare it to a more traditional way of working with Spring Boot.
A request comes in via a Spring Boot controller, which then sends a command or query over Axon’s command gateway or query gateway. These both work with
CompletableFuture. How do I handle errors and convert them to the appropriate HTTP status code?
I am used to converting custom core exceptions (i.e.
ResourceNotFound) to their respective HTTP status code by catching it and throwing a
ResponseStatusException. I haven’t used
CompletableFuture before in this context. What would be a good way to go about this and still keep my application’s core presentation-agnostic?
instead of using try-catch, which is an imperative programming style, you can use the
completableFuture.exceptionallyCompose() methods to translate exceptions from a CompletableFuture to another type of response.
Note that the first allows you to map an exception to a “regular” response, which would be useful if you use Spring’s
ResponseEntity. If you wish to map an exceptionally completed
CompletableFuture to one that is exceptionally completed with a different exception, the second one is more flexible as you can return any new CompletableFuture you wish.
StackOverflow also suggests an approach to map one exception to another one: java - Map exception in completable future to a different exception type? - Stack Overflow
Hope this answers your question.
Added to what @allardbz suggested above, this code-sample might give you some ideas on how to do it!
I’ll check out these options, thanks a lot!
we have a quite similar setup and what we usually do in the REST controller is using the
org.axonframework.commandhandling.gateway.CommandGateway#sendAndWait() variant of the command gateway to send a command and synchronousy wait for the command to complete or throw an exception. We have a central class containing spring web exception handlers for all known exceptions to expect (see Web on Servlet Stack) that transforms an exception into a
ResponseEntity with the appropriate status code. Since we’re using Axon Server to distribute command handling we implemented a
MessageHandlerInterceptor that allows forwarding exception details from remote command handler to the controller (also see this thread: Get the entire stack trace fpr remote command handlers)
If you would use the
org.axonframework.commandhandling.gateway.CommandGateway#send() variant of the command gateway to send the commands in your controller, you would need to block until the completeable future completes to get the exceptional result as described by @allardbz or e.g. return a HTTP 202 accepted response status code to indicate only that the command is submitted (allowing the outcome to be queried with a separate request on the query side).
Thanks for the tips!
Using your @allardbz and @lfgcampos’ suggestions, I am now working on wrapping my custom exceptions in exceptions that AxonIQ understands. This is where a MessageInterceptor will come in handy.
If you would use the
org.axonframework.commandhandling.gateway.CommandGateway#send() variant of the command gateway to send the commands in your controller, you would need to block until the completeable future completes
Actually, Spring supports CompletableFuture as a return type for controller methods, and will switch to Async mode to return the response to the client. No need for any blocking
I typically would just use the
sendAndWait method as has been said already in the thread because it lets you write synchronous and (arguably) more readable code.
Just to add - with Project Loom around the corner the performance of
sendAndWait will not neccesarily be worse than the asynchronous alternative and so the potential performance impact of a synchronous approach may be gone in a future Java + Spring + Axon version.
I’ll look into
sendAndWait! Is something like this also available for the QueryGateway?
You can see the available methods on the query gateway on the published javadoc here: QueryGateway (Axon Framework 4.5.8 API)
It seems like there is no queryAndWait equivalent.
But you could always just call
get() on the
CompletableFuture<R> which is returned. It is annoying you might have to deal with a checked exception, but it is still totally workable.