Search code examples
axon

AggregateMember inheritance: No handler was subscribed to command


I have the below aggregate which contains an aggregate member.

@Aggregate
public class CpaAggregate {

    @AggregateIdentifier
    private String externalId;

    @AggregateMember
    private Entity entity;

    public CpaAggregate() {
    }

    @CommandHandler
    public CpaAggregate(CreateCpaCommand cmd) {
        AggregateLifecycle.apply(new CpaCreatedEvent(...));
    }

    @EventSourcingHandler
    protected void on(CpaCreatedEvent evt) {
      ....
    }

}

public class Entity {

    @EntityId
    private String entityId;
    
    private Set<Identifier> identifiers = new HashSet<>();

    public Entity() {
    }

    @EventSourcingHandler
    public void on(IdentifiantUpdatedEvent evt) {
        ...
    }
    
}

public class Laboratory extends Entity {

    private OperatingSystem operatingSystem;

    public Laboratory() {
    }
    
    @CommandHandler
    public void handle(UpdateIdentifierLABCommand cmd) {
       AggregateLifecycle.apply(new IdentifiantUpdatedEvent(....));
    }
    
}

.

commandGateway.sendAndWait(new UpdateIdentifierLABCommand(...));

When i send a command to update an identifier of entity of type laboratory, i get this error

org.axonframework.commandhandling.NoHandlerForCommandException: No handler was subscribed to command [UpdateIdentifierLABCommand]


Solution

  • I would model your CpaAggregate slightly differently, Aymen.

    Instead of using the generic Entity aggregate member, I'd go for more specific entities like the Laboratory instance.

    This is, for one, a lot more clear modeling-wise, as the model structure becomes clearer. Secondly, Axon Framework will move up into parent class for specifics. So, you can still have common information in an Entity class, like entity identifiers, command handlers, and event sourcing handlers.

    I would thus adjust it like so:

    @Aggregate
    public class CpaAggregate {
    
        @AggregateIdentifier
        private String externalId;
    
        @AggregateMember
        private Laboratory laboratory;
    
        public CpaAggregate() {
        }
    
        @CommandHandler
        public CpaAggregate(CreateCpaCommand cmd) {
            AggregateLifecycle.apply(new CpaCreatedEvent(...));
        }
    
        @EventSourcingHandler
        protected void on(CpaCreatedEvent evt) {
          ....
        }
    
    }
    

    This should ensure Axon Framework spots the command handler inside the aggregate member too, by the way.