Issue
Apologies if this has already been covered, I am new to Axon, and I believe I have read through related Axon documentation and scanned through questions and not found anything which covers my question.
Here is my query...
With Axon, I understand that I can create a command, send it to the gateway, it then forwards it to the correct Aggregate, which then applies an event or two. The events are then persisted and optionally passed on to other event handlers.
(Please note in my case, I'm using Spring Boot with an EmbeddedEventStore
, a SimpleCommandBus
and a JdbcEventStorageEngine
).
With Axon 4, I'm noticing that additional EventHandler
s are being executed in a different thread to the one which handles the command, so presumably there is an expectation that the command has been completed successfully once the Aggregate's command handler method has applied the events (?)
My question really comes down to: how do (or should) systems using Axon handle errors in these downstream event handlers?
For example, a downstream handler may invoke another API - what should happen if that request fails, and is there anything built-in to Axon which provides a good convention for this?
In a non-CQRS/Axon based application, I imagine this would largely be performed by using an external message bus to host event messages and have consumers triggered based on new messages. XA transactions could be used to ensure data consistency (e.g. data written to DB, and message added to queue). I'm not sure if XA transactions are still widely accepted anymore - given how microservices have become more prominent.
With Axon, I suspect the command has completed successfully, even if one of the downstream event handlers fails. I understand Axon is trying to completely separate the two concerns.
Possible solution...?
I could introduce an external Message bus or database to store failed events and retry them, but I cannot help but think this is duplicating what Axon is already doing - storing events and executing code in response to events. It would seem like a lot of work to serialize an event, send to a queue (which could also fail), only for it then to be consumed and then re-played within the application. I'm not sure how I would re-invoke the same handlers even if I deserialized the same event to, say, an EventMessage
.
Does anybody have any thoughts on this, please? Perhaps I'm completely missing the point of CQRS and Axon, and going about things in the wrong way. Please correct if that's the case :).
Thanks
Solution
I am not sure to understand your question correctly.
My question really comes down to: how do (or should) systems using Axon handle errors in these downstream event handlers?
A downstream event handler (in which I assume a separate class implementing one or more @EventHandler
methods) marks an event as "consumed" as soon as the annotated method terminates normally. If an exception is thrown during method execution, the event is considered not consumed. Axon keeps track, if configured to use a persistent token store, of handled events and retries when a previous request to handle an event by the EventHandler method fails. It keeps on trying until it eventually is consumed.
With Axon, I understand that I can create a command, send it to the gateway, it then forwards it to the correct Aggregate, which then applies an event or two. The events are then persisted and optionally passed on to other event handlers.
If you mean 'the events are then persisted' being events are persisted when the AggregateLifecycle.apply
method is called passing the event as argument, then you are right. The event is then persisted in the event store on which other threads are "scanning" to inform @EventHanlder
annotated methods.
With Axon 4, I'm noticing that additional EventHandlers are being executed in a different thread to the one which handles the command, so presumably there is an expectation that the command has been completed successfully once the Aggregate's command handler method has applied the events (?)
Correct! Keep in mind though that commands are "fire and forget". They are not stored in some kind of command store. If a command handler cannot complete or fails for some reason, to bad... A retry should be done by sending a new command. Commands are a request to do some state changing action towards an aggregate but never change the state themselves. It is up to the aggregate to decide if this state update is allowed based on its current state. If so, the aggregate signals an event which will be stored in the event store. That event will again be captured by the aggregate's @EventSourcingHandler
which updates the state. If the request in invalid, no event is applied and the command methods just terminated normally forgetting the command has ever been sent.
Hope this clarifies some of your questions.
Answered By - KDW
Answer Checked By - Terry (JavaFixing Volunteer)