Configure aggregates in order to return id and version

Hello together,

what is best practice to configure axon framework in spring boot, that all aggregates return their Identifier + Version
after applying a command?

Only way I found was defining a class:

`
class VersionReturningAggregateAnnotationCommandHandler extends AggregateAnnotationCommandHandler {

public VersionReturningAggregateAnnotationCommandHandler(Class aggregateType, Repository repository) {
super(aggregateType, repository, new AnnotationCommandTargetResolver());
}

@Override
protected Object resolveReturnValue(CommandMessage<?> command, Aggregate createdAggregate) {
return new VersionedAggregateIdentifier(createdAggregate.identifier().toString(), createdAggregate.version());
}
}
`

which than needs to be configured in this ugly way:

@Autowired public void configure(AxonConfiguration configuration) { for(ModuleConfiguration module : configuration.getModules()) { if(module instanceof AggregateConfigurer<?>) { AggregateConfigurer<?> aggregateConfigurer = (AggregateConfigurer<?>)module; aggregateConfigurer.configureCommandHandler(c -> new VersionReturningAggregateAnnotationCommandHandler( aggregateConfigurer.aggregateType(), aggregateConfigurer.repository())); } } }

Is there a better way to reach this goal ?

Thanks in advance,

  • Benjamin

Hi Benjamin,

actually had to think a while about this one. It’s something that we wanted to implement in Axon generically at some point (that’s why Axon 4 has an explicit CommandResultMessage, allowing us to add additional information in the future).

I think the least intrusive way to implement this, would be using a HandlerEnhancerDefinition. This allows you to alter the behavior of any message handler (Command, Query and Event) in Axon. In your case, you would want to wrap Command handlers with an instance that changes the return value of a handler.
You should be able to use AggregateLifecycle methods from within the definitions.

You will need to register your HandlerEnhancerDefinition using the ServiceLoader mechanism. In a file called “META-INF/services/org.axonframework.messaging.annotation.HandlerEnhancerDefinition” add a line with the fully qualified class name of your implementing class. Axon will then automatically pick it up.

Hope this helps.
Cheers,

Allard

Hi Allard, thanks for the response, we will try that.

Hi,

I tried to implement a HandlerEnhancerDefinition:


@Override
public <T> MessageHandlingMember<T> wrapHandler(MessageHandlingMember<T> original)
{
 return original.annotationAttributes(CommandHandler.class)
 .map(attr -> (MessageHandlingMember<T>) new MethodCommandMessageHandlingMember<>(original, attr))
 .orElse(original);
}

But I found no documentation or example, how I could access the return type of a commandler class in the message handling member class:


private static class MethodCommandMessageHandlingMember<T> extends WrappedMessageHandlingMember<T>
{
 private MethodCommandMessageHandlingMember(MessageHandlingMember<T> delegate, Map<String, Object> annotationAttributes)
 {
 super(delegate);
 }
}

What methods do I need to override and how can I change the return type.

A little guidance would be great,

Thanks in advance,

  • Benjamin

Hi Benjamin,

the HandlerEnhancer is not documented at the same level as other components, because we don’t really consider it part of the standard API. It’s a pretty low-level feature that you can use to tweak things.

The method you’d want to override in this case, is the handle(Message, T) method. T is the actual target instance on which the method will be executed. This is either your aggregate root, or an entity within that aggregate. In either case, you should be able to do AggregateLifecycle.version() and AggregateLifecycle.identifier() to get the version and identifier, which you will want to use to construct your return value.

Hope this helps.
Cheers,

Allard

Hi Benjamin,

I am implementing this requirement in the way describe above but I have an issue during the first command that constructs the aggregate. The target instance of the handle method is null which prevents me from the getting the version of the aggregate.
What did you do to get the version upon creation of the aggregate?

The “ugly” workaround described before?

Robin

Hi Robin,

the constructor is indeed a special case. Instead of passing the aggregate instance as a parameter, it returns the instance as a result. Unfortunately, changing the return value of those methods will be problematic, as the repository expects the created aggregate to be returned from a constructor.

Instead, the AggregateAnnotationCommandHandler is the class that manages the invocations of command handlers defined in Aggregate instances. It has a protected method “resolveReturnValue”, which allows you to customize the return value of constructor command handlers. The reason being that we do not expect anyone to want to return the aggregate instance that was created.

You can use the AggregateConfigurer to configure a custom AggregateAnnotationCommandHandler instance, which overrides this method. We haven’t provided a separate configuration hook for this, just yet.

Cheers,

Hi Allard,

Yes I already got it working with a custom AggregateAnnotationCommandHandler.

A “clean” hook for this or a way to always have the new aggregate version returned would still be nice though. But I understood it is somewhere in the backlog.

Thanks for the answer
Robin