Yet another "Make sure the Aggregate Identifier is initialized before registering events."

3,517 views
Skip to first unread message

Alexandru Stefan

unread,
Dec 14, 2016, 8:11:36 AM12/14/16
to Axon Framework Users
Hello,

I have encountered the AggregateIdentifierNotInitializedException problem, which I am not able to solve. I have tried everything found on this group, and still not sure why it's not working.
The exception occurs after creating the Aggregate, when I try to update it via a Command by using repository load in command handler. 

I am using Axon 2.4.5 and Spring Boot 1.4.2.

Below is how I create the Aggregate

public class IdentityAgg extends AbstractAnnotatedAggregateRoot implements Serializable {

private static final long serialVersionUID = 8832894803205334145L;

@AggregateIdentifier
private String id;

private String type;

private String name;

private List<Contact> contactList;

public IdentityAgg() {}

public IdentityAgg(String id, String type, String name) {
apply(new IdentityCreatedEvent(id, type, name));
}

@EventSourcingHandler
public void handle(IdentityCreatedEvent event) {
this.id = event.getIdentityId();
this.type = event.getType();
this.name = event.getName();
}

And this is how I update the Aggregate 

@Component
public class ContactCommandHandler {

@Autowired
private Repository repository;


@CommandHandler
public void handle(RegisterNewContactCommand registerNewContactCommand) throws OPNSException {
IdentityAgg identityAggToAddWithin = (IdentityAgg) repository.load(registerNewContactCommand.getIdentityId()); // this line is throwing the exception
identityAggToAddWithin.addContact(registerNewContactCommand.getIdentityId(), registerNewContactCommand.getContact());
}

jorgheymans

unread,
Dec 14, 2016, 10:51:57 AM12/14/16
to Axon Framework Users
AFAICT you're missing @CommandHandler on the second constructor.

Jorg

Alexandru Stefan

unread,
Dec 15, 2016, 3:39:18 AM12/15/16
to Axon Framework Users
Thanks for your answer, actually I don't think this is the issue, because the second constructor is not being invoked directly by a command, there is another command which calls it.
Here is the code which calls the second constructor from within a command handler :

@Component
public class IdentityCommandHandler {

@Autowired
private Repository<IdentityAgg> repository;

@CommandHandler
public void handle(CreateIdentityCommand createIdentityCommand {

IdentityAgg identityAgg =
new IdentityAgg(createIdentityCommand.getIdentityId(),createIdentityCommand.getType(),createIdentityCommand.getName());
repository.add(identityAgg);

}
}

Alexandru Stefan

unread,
Dec 15, 2016, 3:58:51 AM12/15/16
to Axon Framework Users
I've forgot to mention that the Aggregate is being stored in the event store, and I am using the JdbcEventStore as an implementation

jorgheymans

unread,
Dec 15, 2016, 5:13:40 AM12/15/16
to Axon Framework Users
I don't think this is supported. The @CommandHandler needs to be on an AR constructor and Axon needs to route the command to that constructor so that it knows to create a new AR in the store.

Creating new Aggregate Instances

When the @CommandHandler annotation is placed on an Aggregate's constructor, the respective command will create a new instance of that aggregate and add it to the repository. Those commands do not require to target a specific aggregate instance. That wouldn't make sense, since the instance is yet to be created. Therefore, those commands do not require any @TargetAggregateIdentifier or@TargetAggregateVersion annotations, nor will a custom CommandTargetResolver be invoked for these commands.

 

Alexandru Stefan

unread,
Dec 15, 2016, 5:42:34 AM12/15/16
to Axon Framework Users
I wish it was that simple :). Anyway I've just modified as you suggested, and this is how my code looks right now. But still no success, even more, now the aggregate is not being inserted into event store anymore.

This is how I send the command :
commandGateway.send(new CreateIdentityCommand(uuid,
identityTypeAndName.getType(), identityTypeAndName.getName()));

This is how my constructor looks now:
@CommandHandler
public IdentityAgg(CreateIdentityCommand createIdentityCommand) {
apply(new IdentityCreatedEvent(createIdentityCommand.getIdentityId(), createIdentityCommand.getType(), createIdentityCommand.getName()));
}

@EventSourcingHandler
public void handle(IdentityCreatedEvent event) {
this.id = event.getIdentityId();
this.type = event.getType();
this.name = event.getName();
}

And this is the command object (p.s: I've already tried to use the @TargetAggregateIdentifier annotation, but no difference :), and by reading the docs, I know why :) )

public class CreateIdentityCommand {


private final String identityId;

private final String type;

private final String name;

public CreateIdentityCommand(String identityId, String type, String name) {
this.identityId = identityId;
this.type = type;
this.name = name;
}

public String getIdentityId() {
return identityId;
}

public String getType() {
return type;
}

public String getName() {
return name;
}
}


Thanks a lot for your help.

Allard Buijze

unread,
Dec 16, 2016, 4:01:25 PM12/16/16
to Axon Framework Users
Hi,

@CommandHandler annotations can be put both on the aggregate itself (recommended), or on an 'external' bean. The latter should only be used if the command required access to external services to be executed. In that case, you may want to fetch this external information before loading (and thus acquiring a lock on) the aggregate instance.

Note that the very first event in an aggregate's event stream must set the AggregateIdentifier. If it doesn't, this exception is thrown. Also make sure that the value you set is non-null. I once spent too many minutes of my life debugging, only to figure out a field was never set in an Event (dev/null setter).

Cheers,

Allard

--
You received this message because you are subscribed to the Google Groups "Axon Framework Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to axonframewor...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alexandru Stefan

unread,
Dec 29, 2016, 9:47:17 AM12/29/16
to Axon Framework Users
Hi Allard,

Thanks for your time, but I am still not able to make it work, in the mean time i have upgraded to axon 3.0-RC2 (using it along with spring boot 1.4.2), and as I am in development mode, I used the InMemmoryEventStorageEngine, but as soon as I switch to JdbcEventStorageEngine I get the "The aggregate identifier has not been set" exception which I guess is the same as the one used in axon 2.4.5

I don't understand what diferrence it makes when using the Jdbc implementation, but for sure it doesn't work.Here you can find my configuration http://pastebin.com/xs5RdftU . And this is how I create a new Agg (the command and the AggRoot) http://pastebin.com/ugNfD4y5 
If you need some more info, please let me know.

I really need your help as I am stuck. I've read the documentation and used the axon bank example without luck. 

Thanks in advance for your patience and help.

Allard Buijze

unread,
Jan 3, 2017, 3:54:45 PM1/3/17
to Axon Framework Users
Hi Alexandru,

it's weird that changing the StorageEngine changes the behavior of applying the event. The only thing I can image happening, is that an exception is thrown somewhere that prevents the identifier to be set, or recognized. I'd expect a stacktrace in the logs, but when things seem "weird", I prefer not to make any assumptions at all.

Did you try configuring the JPAEventStorageEngine? I'm curious to see whether that one works.

When using Spring, use the SpringDataSourceConnectionProvider instead of the DataSourceConnectionProvider.  The former will also check any existing connection used in the a Spring-managed transaction.

Cheers,

Allard

--

Alexandru Stefan

unread,
Jan 9, 2017, 4:28:05 AM1/9/17
to Axon Framework Users
Hi Allard,

Sorry for the late response, I've been in a small holiday.

So, I've updated to Axon 3.0 (no more RC-2) and added JpaEventStorageEngine instead of Jdbc. Unfortunately I have the same issue.
By the way I am using Oracle as underlying databse.

The exception occurs in EventSourcedAggregate in the publish method.

Please let me know what other info is needed in order to be able tu investigate further this problem.

Thanks !


@Override
protected void publish(EventMessage<?> msg) {
if (msg instanceof DomainEventMessage) {
lastEventSequenceNumber = ((DomainEventMessage) msg).getSequenceNumber();
}
snapshotTrigger.eventHandled(msg);
super.publish(msg);
if (identifierAsString() == null) {
throw new IncompatibleAggregateException("Aggregate identifier must be non-null after applying an event. " +
"Make sure the aggregate identifier is initialized at " +
"the latest when handling the creation event.");
}
}

Alexandru Stefan

unread,
Jan 9, 2017, 6:07:00 AM1/9/17
to Axon Framework Users
I have investigated further trying to understand what is really going on. 

So far I've found that no matter what AggregateFactory I use (either Generic or SpringPrototype) somehow in createAggregateRoot method the Aggregate is never initialized with the DomainEventMessage firstEvent.

In the GenericAggregateFactory if (aggregateBaseType.isAssignableFrom(firstEvent.getPayloadType())) statement is getting always false, because the agregate base type is of type "IdentityAgg" and the event payload type is "IdentityCreatedEvent", which is correct, but that means a new instance of IdentityAgg will be created, which will lead to the indentifier to be null causing the exception.

Also in SpringPrototypeAggregateFactory the createAggregateRoot method returns an IdentityAgg but with null fields, also triggering the above mentioned exception

I might be wrong, but this is all I could found by debugging trough axon core :)

Allard Buijze

unread,
Jan 11, 2017, 11:10:32 AM1/11/17
to Axon Framework Users
Hi Alexandru,

the AggregateFactory should deliver initialized aggregates, so it's fine that the value for the identifier is null at that point. This exception is raised when the identifier is not set to a non-null value after the first event has been applied (IdentityCreatedEvent in your case). That means IdentityCreatedEvent must be the first event, and the handler for that event must set the annotated identifier to a non-null value.

That said, is you identifier field annotated with @AggregateIdentifier?

Cheers,

Allard

Alexandru Stefan

unread,
Jan 11, 2017, 11:43:45 AM1/11/17
to Axon Framework Users
Hi again :)

You can see here in this paste http://pastebin.com/ugNfD4y5 that the identifier has the mentioned annotation, also you can see the Eventhandler for the IdentityCreatedEvent.
The configuration can be found here 

I agree with you but I am still a little bit confused, looking at the following method, I understand the following things :

    @Override
    protected EventSourcedAggregate<T> doLoadWithLock(String aggregateIdentifier, Long expectedVersion) {
        DomainEventStream eventStream = eventStore.readEvents(aggregateIdentifier);
        SnapshotTrigger trigger = snapshotTriggerDefinition.prepareTrigger(aggregateFactory.getAggregateType());
        if (!eventStream.hasNext()) {
            throw new AggregateNotFoundException(aggregateIdentifier, "The aggregate was not found in the event store");
        }
        EventSourcedAggregate<T> aggregate = EventSourcedAggregate
                .initialize(aggregateFactory.createAggregateRoot(aggregateIdentifier, eventStream.peek()),
                            aggregateModel(), eventStore, trigger);
        aggregate.initializeState(eventStream);
        if (aggregate.isDeleted()) {
            throw new AggregateDeletedException(aggregateIdentifier);
        }
        return aggregate;
    }

 1. First, the event stream is loaded from the eventstore (means it goes to the database and it gets all the events)
 2. If there are no events it means that there is no aggregate with the specified identifier
 3. Initialize the aggregate based on the stream ( bolded in the method above ) . Here the returned object has the identifier null, and you say this is normal :)
 4. Initializing the state by calling the "publish" method for each event from event stream, and inside the "publish" method the exception is thrown. But again using the debugger the identifier it is still null, and not just the identifier, but all of the fields from within the IdentityAgg.

Checking forward I got in ModelInspector class which uses ReflectionUtils to get the field value :

@Override
    public Object getIdentifier(T target) {
        if (identifierField != null) {
            return ReflectionUtils.getFieldValue(identifierField, target);
        }
        return null;
    }

Here, the target object is an empty IdentityAgg so the method getFieldValue tries to get the identifier field from an empty target.

If you need some more information, please let me know, I already implemented a lot using Axon (used the in memory event store, so I couln't catch the issue earlier) , and giving up using it is not a choice for me, so I need to solve it somehow :).

Btw thanks for making such a cool framework open source.

Regards,
Alex

Allard Buijze

unread,
Jan 11, 2017, 3:43:15 PM1/11/17
to Axon Framework Users
Hi Alex,

I've tried to reconstruct your application based on the pastbin stuff you posted. It all works here. I'm unable to reproduce the issue.
If you have a means to share more of your application with me, that might help finding the issue.

Cheers,

Allard

Alexandru Stefan

unread,
Jan 12, 2017, 9:54:57 AM1/12/17
to Axon Framework Users
I wish I could share you more, but I have no idea how.

Most probably it works as you don't use an Oracle Database.

Going further with the investigation I might found something new :) Maybe this will give you a hint on what is going on.

When the super.publish(msg)  is called here :

    @Override
    protected void publish(EventMessage<?> msg) {
        if (msg instanceof DomainEventMessage) {
            lastEventSequenceNumber = ((DomainEventMessage) msg).getSequenceNumber();
        }
        snapshotTrigger.eventHandled(msg);
        super.publish(msg);
        if (identifierAsString() == null) {
            throw new IncompatibleAggregateException("Aggregate identifier must be non-null after applying an event. " +
                                                             "Make sure the aggregate identifier is initialized at " +
                                                             "the latest when handling the creation event.");
        }
    }

At a certain point a Handler for my IdentityCreatedEvent is searched in ModelInspector class in getHandler method, more specifically here :

    protected Optional<MessageHandlingMember<? super T>> getHandler(Message<?> message) {
        for (MessageHandlingMember<? super T> handler : eventHandlers) {
            if (handler.canHandle(message)) {
                return Optional.of(handler);
            }
        }
        return Optional.empty();
    }

The method returns Optional.empty() which is wrong due to fact that it has to process the event in order to reconstruct the state. And btw this is not happening while using the InMemmory event store, which is extremely strange.

In the eventHandlers collection I can see the corect eventhandler for my IdentityCreatedEvent, and when the handler.canHandle(message) is called, is returning false because all the conditions in bold from the method below are returning false. Not being able to find a handler it makes me think that the state of the Aggregate can't be reconstructed.

    @Override
    public boolean canHandle(Message<?> message) {
        return typeMatches(message) && payloadType.isAssignableFrom(message.getPayloadType()) &&
                parametersMatch(message);
    }

The strangest thing is that payloadType and message.PayloadType are the same

payloadType = class ro.orange.opns.publisher.cqrs.events.IdentityCreatedEvent
message.getPayloadType = class ro.orange.opns.publisher.cqrs.events.IdentityCreatedEvent
message is of type class org.axonframework.eventsourcing.GenericTrackedDomainEventMessage

P.S: I can't make the code public, so please let me know if you have some other suggestion on how to share more with you.

Thanks Allard for your patience.

Allard Buijze

unread,
Jan 12, 2017, 10:26:09 AM1/12/17
to Axon Framework Users
Hi,

so basically, on the breakpoint, you have two classes (can you see their ID? Usually after the @) that seem to be the same, but aren't. This can be the case when you use a special classloader.

I have seen this once before when using Spring Boot DevTools. It may also happen with other frameworks that do automatic reloading for you. In some cases, they reload the Event classes, but not the aggregates, causing a situation like this.

Let's hope we're on a path to a solution here....
Cheers,

Allard

--

Alexandru Stefan

unread,
Jan 12, 2017, 11:57:36 AM1/12/17
to Axon Framework Users
Hi,

I have attached a print screen with the debugger on, so it looks like you are right, they have different id's. So that was the problem.

I do have Spring Boot DevTools, so basically that was the issue. 
Packaging the application into a .jar file, and it works, also I removed the dependency so I can still use debug mode :)

Well, this was a hell of an issue :) Hopefully it will help other people too. Thanks a lot Allard for helping me solving the problem !

P.S: maybe you should mention this situation somewhere in the docs :).

2017-01-12_18-09.jpg

Allard Buijze

unread,
Jan 13, 2017, 8:46:35 AM1/13/17
to Axon Framework Users
Hi Alexandru,

the devtools issue has come across before, but didn't yet manifest itself in this way. That's why it took a little while to figure out what was going on.

I've been trying to figure out why devtools doesn't seem to work with the way Axon resolves annotations, but don't have any success so far. One day, I'll get it sorted out....

I'll put a warning about this in the ref guide.

Cheers,

Allard

Allard Buijze

unread,
Mar 10, 2017, 8:06:12 AM3/10/17
to Axon Framework Users
FYI: The issue has been found and fixed and a solution will be part of the next release.  

Alexandru Stefan

unread,
Mar 10, 2017, 8:17:13 AM3/10/17
to Axon Framework Users
That's good news, thanks !

Samuele Di Rito

unread,
Mar 22, 2017, 6:07:13 AM3/22/17
to Axon Framework Users
Allard, please, can you confirm this is the related issue?
Thank you very much.

Allard Buijze

unread,
Mar 22, 2017, 12:12:26 PM3/22/17
to Axon Framework Users
Hi,

actually, it's this one.

Cheers,

Allard

--
Reply all
Reply to author
Forward
0 new messages