[Axon 3.2] CommandCallback with Dispatch/Handler interceptors


I just have a question about the difference of behavior when using Dispatch and Handler interceptors with a FutureCallback.

Let’s take a concrete example :

I setup a BeanValidationInterceptor as a MessageDispatchInterceptor on the SimpleCommandBus, and use the DefaultCommandGateway in front of the SimpleCommandBus, using the

*<R> CompletableFuture<R> send(Object command)* method (so I can easily switch to an **AsynchronousCommandBus** in the future if needed).

I can see that in that case, a FutureCallback is indeed created in the background so that we are able to get the result of the command execution asynchronously.

This makes it possible to handle errors on the client side, as following for example :

            new DoSomethingCommand(id))
            .isCompletedExceptionally()) { //do something }

But I also notice that the FutureCallback is not passed to the dispatch interceptors chain, meaning that this condition will never be true even in case of validation errors (as *completeExceptionnally* won't be called on the FutureCallback instance).

On the other hand, if I setup the **BeanValidationInterceptor** as a **MessageHandlerInterceptor**, the FutureCallback is correctly called as it's passed to the message handler interceptors chain.

Is it designed like this on purpose ? I understand that the MessageDispatchInterceptor is always executed on the thread that dispatches the Command but wouldn't it be more consistent to notify the callback in that case as well ?

Thank you,
Best Regards,


Hi Jerome,

yes, this actually is by design. Dispatch interceptors are invoked, by (our) definition in the thread that dispatches the command. Handler Interceptors do that in the thread that handles the command. In distributed scenarios, these threads live on different machines. In simple scenarios, these happen to be the same thread.

When an exception occurs while dispatching the command (i.e. you component didn’t manage to get in on the Command Bus), that exception is thrown, to be caught by the caller. If the message did make it onto the bus, the results are reported using the Callback or CompletableFuture, depending on the API you’re using.

That said, we have had discussions in our team about making the error handling case for sending components more consistent. In Axon 3, we chose behavior consistent with previous versions. For the next major release, we’re going to review this decision, again. We’re open to arguments for either case.