My understanding is that AxonServer must be contacted first in any case, even if the command routing algorithm selects the JVM, which dispatched the command originally. I believe this is the source code responsible for that part.
I indeed agree with you but was making a guesstimate based on what I pulled from your description. I now notice that was a wrong assumption from my part, based on the following line in your original message:
Method returns without complaints when AxonServer is not running.
This to me sounds like everything worked fine, but I believe what you intended to share is that the
CompletableFuture returned, albeit with an exceptional result.
Back to the actual thing you are investigating,
sendAndWait() will bubble up the exception, as you have likely noticed. Internally
sendAndWait() is nothing more than the
send() operation, where the framework waits for the outcome of the
CompletableFuture for you.
This indeed makes your assumption correct. Not checking the outcome of the
CompletableFuture will simply not land you the
CommandDispatchException directly in your face. You will instead have to check when the future is complete yourself if this is required. Or let another component, like Spring’s Web UI, resolve it for you.
I’d wager the definition of “received” in the documentation is what put you off in this case. I would say you did provide the command to the
CommandBus inside your JVM by any means. Whether it went from one end to another, so if dispatching succeeded, is still up for debate.
Maybe we should turn this situation around, by going back to your original statement:
But I’d like to respond to clients with an error if the command wasn’t ACKed by bus.
Followed up by your first question:
Is there any way to distinguish command dispatching failures from command handling failures?
I would assume that either, regardless of whether you use
sendAndWait on the
dispatch(C, CommandCallback<C, R>) on the
CommandBus, can be achieved by checking for the aforementioned exceptions in my previous response. The checking approach is just different:
CommandGateway#send(Object) - The exception from the
CompletableFuture will be rethrown from, so you can add a try-catch if needed.
CommandGateway#sendAndWait(Object) - The outcome is present in the
CompletableFuture, which should be validated in your set up
CommandBus#dispatch(C) - This would net you, sadly, nothing, as the
CommandCallback used in this case is a no-op
CommandBus#dispatch(C, CommandCallback<C, R>) - The result is returned to the
CommandCallback#onResult, where the
CommandResultMessage should be validated.
By the way, if you feel the description is too ambiguous right now, any improvements are always fair game as pull requests of course. Furthermore, I hope this clarifies your option further @Beka_Tsotsoria. If anything else is still open, definitely reach out.