2
votes

I am getting the below error. Axon version 3.3.3

org.axonframework.eventsourcing.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.

I have created a UserAggregate. It contains 2 events:

  • UserCreated
  • UpdateUserEvent

I am able to generate the first (UserCreated) event and it was saved in the event store with sequence 0, But while generating the second event I got the above-mentioned error.

Any suggestions on this?

UserAggregate.java

@Aggregate
public class UserAggregate {

    @AggregateIdentifier
    private String id;


    private String email;
    private String password;

    public UserAggregate(String id, String email, String password) {
        super();
        this.id = id;
        this.email = email;
        this.password = password;
    }

    @CommandHandler
    public UserAggregate(CreateUser cmd) {
         AggregateLifecycle.apply(new UserCreated(cmd.getId(), cmd.getEmail(), cmd.getPassword()));
    }

    @CommandHandler
    public void handle(UpdateUserCmd cmd) {
         AggregateLifecycle.apply(new UpdateUserEvent(cmd.getId(), cmd.getEmail(),""));
    }

    @EventSourcingHandler
    public void userCreated(UserCreated event) {

        System.out.println("new User: email " + event.getEmail() +" Password: "+ event.getPassword());

        setId(event.getId());




    }

    @EventSourcingHandler
    public void updateUserEvent(UpdateUserEvent event) {

        System.out.println("new User: email " + event.getEmail());

        setId(event.getId());




    }


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public UserAggregate() {
    }

}
3

3 Answers

1
votes

I am still getting to know Axon but here's how I managed to resolve the issue. Basically what the error is saying is that, when the UserAggregate is being instantiated the aggregate identifier (aka Primary Key) must not be null and must have a value.

There sequence of the life-cycle is that

  1. It calls a no args constructor
  2. It calls the constructor with your initial command, in your case. At this point, your aggregate identifier is still null and we will assign a value in the next step
  3. It then calls a EventSourcingHandler that handles the event your applied from the previous step

Based on the steps above here's what you need to do:

  1. Create a no args constructor

protected UserAggregate() {

}
  1. Create a constructor which handles your first command:

@CommandHandler
public UserAggregate(CreateUser cmd) {
    AggregateLifecycle.apply(
         new UserCreated(cmd.getId(),cmd.getEmail(),cmd.getPassword()));
}
  1. Finally add an event sourcing handler to handle the UserCreated event

@EventSourcingHandler
public void on(UserCreated userCreated) {

    // this is where we instantiate the aggregate identifier
    this.id = userCreated.getId();
    
    //assign values to other fields
    this.email = userCreated.getEmail();
    this.password = userCreated.getPassword();

}

And here's the complete example:


@Aggregate
public class UserAggregate {

    @AggregateIdentifier
    private String id;

    private String password;

    private String email;

    protected UserAggregate() {

    }

    @CommandHandler
    public UserAggregate(CreateUser cmd) {
        AggregateLifecycle.apply(
            new UserCreated(cmd.getId(),cmd.getEmail(),cmd.getPassword()));
    }

    @EventSourcingHandler
    public void on(UserCreated userCreated) {

        // this is where we instantiate the aggregate identifier
        this.id = userCreated.getId();

        //assign values to other fields
        this.email = userCreated.getEmail();
        this.password = userCreated.getPassword();

    }
}

0
votes

When you are following the Event Sourcing paradigm for your Aggregates, I'd typically suggest two types of constructors to be present in the code:

  1. A default no-arg constructor with no settings in it.
  2. One (or more) constructor(s) which handles the 'Aggregate creation command'

In your example I see a third constructor to set id, email and password. My guess is that this constructor might currently obstruct the EventSourcedAggregate implementation for correct validation.

The exception you are receiving can only occur if the @AggregateIdentifier annotated field is not set after the constructor command handler (in your case UserAggregate(CreateUser) has ended it's Unit of Work. Thus, seeing your code, my only hunch is this "wild, unused" constructor which might obstruct things.

Lastly, I need to recommend you to use a more recent version of Axon. 3.3.3 is already quite far away from the current release, being 4.2. Additionally, no active development is taking place on Axon 3.x versions. It is thus wise to upgrade the version, which I assume shouldn't be a big deal as you are still defining your Command Model.


Update

I've just closed the Framework issue you've opened up. Axon provides entirely different means to tie in to the Message dispatching and handling, giving you cleaner intercept points than (Spring) AOP.

If you following the suggested guidelines to use a MessageDispatchInterceptor/MessageHandlerInterceptor or the more fine grained option with HandlerEnhancer, you can achieve these cross-cutting concerns you are looking for.

As far as logging goes, the framework even provides a LoggingInterceptor to do exactly what you need. No AOP needed.

Hope this helps you out Narasimha.

-1
votes

Thank you @Steven for the response.

I am able to reproduce this issue with Axon 4.2(latest) version also. After removing the below AOP code in my project, The issue solved automatically. Looks like Axon is missing compatible with the AOP feature.

AOP Code:

@Around("execution(* com.ms.axonspringboot..*(..))")
    public Object methodInvoke(ProceedingJoinPoint jointPoint) throws Throwable {
        LOGGER.debug(jointPoint.getSignature() + "::: Enters");
        Object obj = jointPoint.proceed();
        LOGGER.debug(jointPoint.getSignature() + "::: Exits");
        return obj;
    }

Axon 4.2 version error logs

2019-10-07 12:52:41.689  WARN 31736 --- [ault-executor-0] o.a.c.gateway.DefaultCommandGateway      : Command 'com.ms.axonspringboot.commands.UpdateUserCmd' resulted in org.axonframework.commandhandling.CommandExecutionException(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.)
2019-10-07 12:52:41.710 ERROR 31736 --- [nio-7070-exec-3] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] threw exception

org.axonframework.axonserver.connector.command.AxonServerRemoteCommandHandlingException: An exception was thrown by the remote message handling component: 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 org.axonframework.axonserver.connector.ErrorCode.lambda$static$8(ErrorCode.java:84) ~[axon-server-connector-4.2.jar:4.2]
    at org.axonframework.axonserver.connector.ErrorCode.convert(ErrorCode.java:180) ~[axon-server-connector-4.2.jar:4.2]