Entity Stored Aggregate doesn't apply HandlerEnhancerDefinition members for commands

I created a simple Entity Stored Aggregate

@Entity
@Aggregate
public class FoodBag {
    @Id
    @AggregateIdentifier
    private UUID foodBagId;

   @CommandHandler
    public FoodBag( CreateFoodBag create, MetaData metaData ) {
        apply( new FoodBagCreated( create.foodBagId() ), metaData );
    }

    @CommandHandler
    public void handle( SelectProduct select ) {
        if ( confirmed ) {
            throw new IllegalStateException(
                    "Cannot add a product to a Food Bag order which is already confirmed"
            );
        }

        apply( new ProductSelected( foodBagId, select.productId(), select.quantity() ) );
    }
}

I am using Axons Open-Telemetry library and I noticed only the constructor @CommandHandler is getting properly traced. All other normal handlers are not. The TracingHandlerEnhancerDefinition is properly wrapping all command handlers with the expected span creation.

    public Object handle(@Nonnull Message<?> message, T target) throws Exception {
   return TracingHandlerEnhancerDefinition.this.spanFactory.createInternalSpan(() -> TracingHandlerEnhancerDefinition.getSpanName(target, signature)).runCallable(() -> super.handle(message, target));
}

I tracked it down to an apparent issue in the class AggregateAnnotationCommandHandler. The constructor handles commands using nested AggregateConstructorCommandHandler class which properly uses the wrapped MessageHandlingMember<?> handler field.

However, all other commands handlers use the nested class AggregateCommandHandler which has the same MessageHandlingMember<? super T> handler but it does not use it in the handler method.

    public Object handle(CommandMessage<?> command) throws Exception {
            VersionedAggregateIdentifier iv = AggregateAnnotationCommandHandler.this.commandTargetResolver.resolveTarget(command);
            return AggregateAnnotationCommandHandler.this.repository.load(iv.getIdentifier(), iv.getVersion()).handle(command);
}

This seems to be preventing any tracing for these commands.


The first image shows the Constructor command handler is properly records.
The second images shows the normal command handlers is not.

Can you confirm this is a bug?

Thanks

Do you use Spring autoconfiguration to configure the components you need, or did you use the Configration API from Axon Framework itself?

If Spring, do you have a Repository configured for the FoodBag aggregate, or did you rely on AutoConfiguration to configure it?

Yes, I use Spring AutoConfiguration and rely on the AutoConfiguration. I debugged and saw all the proper repository creation and TracingHandlerEnhancerDefinition application. The AggregateCommandHandler just doesn’t seem to use the wrapped member like AggregateConstructorCommandHandler does.

I just quickly tested it locally with my bike rental demo, and everything seems to work as expected.
The AggregateCommandHandler doesn’t use the wrapped member because it has an initialized Aggregate instance to delegate to. That Aggregate instance itself already has the wrapped handlers configured.

When debugging locally, I see the TracingHandlerEnhancerDefinition invoked as expected. Even on regular command handler methods.

Can you confirm there is no other configuration than the Aggregate you mentioned above?

Can you also confirm that the repository is created using the SpringAggregateConfigurer.configureModule() method?

Last thing to double check: which Axon version are you on?

That’s great info. Must be something i’m doing then :slight_smile: I’ll dig a little more. I am using 4.10.3 of the framework.

So I forgot I had to configure a Repository for FoodBag so it uses UUID and not String for Id

@Configuration
public class FoodOrderConfig {

    // This is needed since Axon uses String identifiers by default

    @Bean
    public Repository<FoodBag> foodBagRepository(
            EventBus eventBus,
            EntityManagerProvider entityManagerProvider,
            ParameterResolverFactory parameterResolverFactory,
            org.axonframework.config.Configuration configuration,
            SpanFactory spanFactory
    ) {
        return GenericJpaRepository.builder( FoodBag.class )
                .eventBus( eventBus )
                .entityManagerProvider( entityManagerProvider )
                .repositoryProvider( configuration::repository )
                .parameterResolverFactory( parameterResolverFactory )
                .identifierConverter( UUID::fromString )
                .build();
    }
}

Would this prevent the tracing from occuring? If so, how do I configure it? It has a handleDefinition option but not HandlerEnhancerDefinition.

In the builder for the repository, add configuration for the handlerDefinition():
.handlerDefinition(configuration.handlerDefinition(FoodBag.class))

return GenericJpaRepository.builder( FoodBag.class )
                .eventBus( eventBus )
                .entityManagerProvider( entityManagerProvider )
                .repositoryProvider( configuration::repository )
                .handlerDefinition(configuration.handlerDefinition(FoodBag.class))
                .parameterResolverFactory( parameterResolverFactory )
                .identifierConverter( UUID::fromString )
                .build();

I’m pretty sure that was the missing piece.

I know this type of configuration doesn’t win the Nobel prize :slight_smile:. We’re changing the way configuration is done/tuned in Axon 5 to make it much easier.

That was it!! Sorry for the false alarm…
And thanks so much for working through this with me on this one.

Is there any way to just add an identifierConverter globally so it won’t be necessary to specialize each repository?

I already had a quick look before posting my last reply. Unfortunately, there isn’t.

Thats fine. The problem is solved and I think Axon is one of the most (if not most) pleasant framework to work with. Definitely done for developers with every opportunity to customize.