Events raised in event handlers and IllegalStateException: 'Cannot set first sequence number if events have already been added'

Hi,

I am relatively new to Axon so please forgive any basic errors!

When trying to construct a complex aggregate, I am first creating the aggregate root which extends AbstractAnnotatedAggregateRoot like so:

MyComplexAggregateRoot(AggregateIdentifier identifier) {
super(identifier);
}

public MyComplexAggregateRoot(AggregateRootDto aggregateRootDto) {
super(new UUIDAggregateIdentifier());
apply(new MyComplexAggregateRootCreatedEvent(aggregateRootDto));
}

@EventHandler
private void onCreate(MyComplexAggregateRootCreatedEvent event) {

// business logic to create aggregate structure (uses builder pattern)
}

I am also catching the ‘MyComplexAggregateRootCreatedEvent’ event in an event handler in the query side so that I can persist a representation of the ‘MyComplexAggregateRoot’ object in the query database.
This is all ok and working as expected.

The aggregate itself contains many objects that are constructed in the ‘onCreate’ event handler, I also need information from these objects in my query database and this information is not available when the MyComplexAggregateRootCreatedEvent event is constructed. I am therefore registering/applying further events when these objects are created. This works as expected and the object structure and query database representation are generated as expected. However, when the aggregate root is loaded from the repository (i.e. reconstructed from the events in the repository), this causes and IllegalStateException:

14:14:51.108 [main] WARN o.a.repository.LockingRepository - Exception occurred while trying to load an aggregate. Releasing lock.
java.lang.IllegalStateException: Cannot set first sequence number if events have already been added
at org.axonframework.util.Assert.state(Assert.java:40) ~[axon-core-1.0.jar:na]
at org.axonframework.domain.EventContainer.initializeSequenceNumber(EventContainer.java:109) ~[axon-core-1.0.jar:na]
at org.axonframework.domain.AbstractAggregateRoot.initializeEventStream(AbstractAggregateRoot.java:128) ~[axon-core-1.0.jar:na]
at org.axonframework.eventsourcing.AbstractEventSourcedAggregateRoot.initializeState(AbstractEventSourcedAggregateRoot.java:94) ~[axon-core-1.0.jar:na]
at org.axonframework.eventsourcing.EventSourcingRepository.doLoad(EventSourcingRepository.java:118) ~[axon-core-1.0.jar:na]
at org.axonframework.eventsourcing.EventSourcingRepository.doLoad(EventSourcingRepository.java:54) ~[axon-core-1.0.jar:na]
at org.axonframework.repository.AbstractRepository.load(AbstractRepository.java:70) [axon-core-1.0.jar:na]
at org.axonframework.repository.LockingRepository.load(LockingRepository.java:119) ~[axon-core-1.0.jar:na]
at org.axonframework.repository.AbstractRepository.load(AbstractRepository.java:80) [axon-core-1.0.jar:na]

This seems to be because of the events generated in the onCreate() handler.

I have also tried adding a separate command and corresponding event to build the ‘MyComplexAggregateRoot’ object after it has been created but this suffers from exactly the same problem. So it seems that you cannot register/apply new DomainEvents in the call hierarchy when handling an event - is this the case?

Is it possible to mark such events so that they are ignored when the object is reconstructed from the event repository?

I think I am probably using events incorrectly here. I guess the event is not really a ‘DomainEvent’ as such as it is used to indicate to the query repository (or whoever is listening) that an object has been created (so that any info needed by the query layer can be created). However, it is not really an Application or System event either (as far as I can tell!).

Is there a standard way to generate complex aggregates and have information about these aggregates persisted for use by the query layer?

Thanks in advance for any help/hints provided.

Regards,

John

Hi John,

applying new events inside an Aggregate’s Event Handler is not the right way to go. In Event Sourcing (not matter how you do it), there is two types operations involved:

  • Decision making operations. They take parameters and decide, based on current state, what should happen with the aggregate
  • State changing operations. They take the results from the decision making methods and apply the state changes involved with its decision.

In Axon, the decision making operations are the only ones allows to apply() an Event. The state changing operations should just change some fields to reflect that event.

Probably what you’ll need to do is:

public MyComplexAggregateRoot(AggregateRootDto aggregateRootDto) {
super(new UUIDAggregateIdentifier());
apply(new MyComplexAggregateRootCreatedEvent(aggregateRootDto));
take some more decisions…
apply(new AnotherTypeOfEvent(aggregateRootDto));
}

I hope this clarifies it a littlebit. If not, don’t hesitate to let me know what bothers you.

Cheers,

Allard

Hi Allard,

thanks for the quick response. I have now got something working - after a fashion! - but will take on board your comments and do some re-factoring.

I think the main problem I am having is that I am trying to apply the Axon framework to an existing domain that wasn’t developed with event sourcing in mind. It has been developed from DDD principles so hopefully it won’t be too much work to refactor to the event sourcing/axon paradigm.

Cheers,

John