Failed aggregate construction still applies events

Hi,

I’m using Axon 3.0.6 and have a very simple non-event sourced JPA aggregate of the form shown in the docs (command-model.html). I dispatch a command and supply a CommandCallback so I can record success and failure.

The aggregate is a simple JPA Entity and correctly fails to be persisted if the aggregate with the given Id already exists (the Id is an email address). The command callback’s onFailure() method is called as expected. However, the event is still applied in the aggregate’s constructor - even though the command failed - causing other components to handle the event. I was expecting that the event would not be applied due to the command failure. Is my understanding incorrect? Or do I need to look more closely at my code/config?

Thanks

Paul

Hi Paul,

in the order of things, Axon can only know there is a duplicate when the aggregate is stored. An aggregate will only be stored after it has applied (or published) a number of events.

Axon runs all activity within a Unit of Work. Generally, a Transaction is attached to this unit of work, and will be committed when the Unit of Works completes successfully, or rolled back in case of failure.
The Event Store/Bus is also aware of this Unit of Work. It will actually only publish events in the “onPrepare” phase. This is also the phase where the aggregate is persisted. It comes to timing which of the two is invoked first, but it is certain that handlers will handle the events in the scope of the transaction in process. If handlers are configured in a Tracking Processor, they will never see the event, because the transaction was never committed.

It is actually only in constructor based command handlers that this timing happens this way. Reason is that the constructor schedules an event for publication, before it returns. It is only after this method returns, that Axon is capable of storing the state.

I hope this clarifies things.
Cheers,

Allard

Hi Allard,

Many thanks for the explanation. So if I understand correctly, this is only a scenario I’m likely to find only with construction of new aggregates? If there was any persistence failure updating any existing aggregate then the transaction would roll back and the events would not be published?

As I use Spring’s AMQP messaging it seems TrackingProcessors are not an option. So instead I just use a Spring Data repository to check for the existence of the aggregate (JPA entity) before creating the command. I did wonder if I could use the Axon repository that is created to store the aggregates, but I didn’t see any API method for checking for the existence of an entity by its Id.

Paul

Hi Paul,

that’s correct: it will only happen in constructors. Quite often the constructors give us a little “catch-22” situation, as we want to prepare things based on the instance before invoking the constructor, but only have the instance after invoking it… Some day, we’ll find a solution…

SpringAMQP only works with SubscribingProcessors. Tracking Processors generally read directly from the event store, basically reading straight from the (reliable) source.

Cheers,

Allard

Would it work to make the aggregate-creation command handlers regular methods rather than constructors, possibly with an additional annotation to mark them as creation handlers? This approach seems to work well for sagas (the @StartSaga annotation) and with that approach, Axon could inject dependencies into aggregates by calling setter methods rather than using command handler method arguments, which seems to be a common point of confusion for new Axon users.

-Steve

Hi Steve,

that’s the line of thinking that I was also following. It would also allow to address the common ‘load or create’ usecase. If repositories would have an ‘exists’ method, it would allow for a relatively cheap way to implement it.

This would also allow for aggregate instances to be Spring managed and injected. It also removes the need for any constructors to be defined on the Aggregate.

I’ll be playing around with this as an alternative. It should be fairly easy to implement using existing MessageHandlerEnhancer mechanisms.

Cheers,

Allard