How can I prevent Asynchronous EventHandler handles events that should be rollbacked

Hi, all:

I’m writing some learning tests to understand CQRS and Axon-framework and I have an question about asynchronous event processing.

Let’s say I have two event handler on CustomerRegisteredEvent, one is synchronous to prevent duplicated customer.

@EventHandler public void on(CustomerRegisteredEvent event) { CustomerReadModel c = transform(event); entityManager.persiste(c);//insert a customer in a relational query database }

The other is to send an email asynchronously.

@EventHandler public void on(CustomerRegisteredEvent event) { System.err.println(Thread.currentThread().getId() + " sending email async"); }

Ideally, I want the email is send only when the customer is registered(ideally, inserted a row in query database). On the synchronous side, it’s fine when exception occurrs, all data is rolled back(including the event), But once the async handler receives the event, it is impossible to rollback and it makes no sense.

How can I avoid this issue. After pondering for a while, I think it’s a difficult one. when an event is published to the bus, the unit of work is committed. so all the handlers(no matter sync or async) will receives the event. Unless all async handlers subscribe another event bus and the event is dispatched to this bus after all sync (and transactional) handler finish their work.

An alternative solution is eventual consistency, do some compensating when exceptions occurrs.

By the way, it seems only infrastructure error causes this problem, if I add an intentional exception to the sync handler like:

@EventHandler public void on(CustomerRegisteredEvent event) { CustomerReadModel c = transform(event); entityManager.persiste(c);//insert a customer in a relational query database throw new RuntimeException("intentionally throw"); }

Then the async handler is not invoked. Is this just a coincidence?

I would like your opinoins, thank you in advance.

From a quick browse of the Axon documentation (I haven't written an application with it yet), it appears to follow the same nomenclature as most other CQRS/ES information "out there" on the web: Commands are where you can stop things from happening, Events are things that already happened. Thus, you can't stop something from happening in an Event handler (it is too late… all you can do is successfully understand what happened and take some followup action).

So I think the answer is: If you want to stop a customer from registering, you do that in a command handler. That is where you can throw an exception of the action is not OK.

Hi,

this one pops up every once in a while, although there is a slight twist this time. Kyle is right about the Command handlers being the only one that can “reject” the execution. However, if you’re using Event Sourcing, validating that a username is unique (a.k.a Set validation) is a slight bit harder.

Basically, there are four aproaches:

  • Have the command handler execute a query to validate uniqueness. Just accept the very small chance of a duplicate when two users register the same name at appx the same time
  • Slight modified version of the above, where the command handler keeps a “used usernames” table, in which it updates and reads the usernames being used.
  • Detect the duplicates in the Event Handler that updates the query model. When a duplicate is detected, the second account is blocked. In your case, this would mean an email is sent.
  • Use a 2-step process where a Saga confirms an account. You could send the email based on the confirmation.

Hope this helps.
Cheers,

Allard