Configure aggregates in order to return id and version

154 views
Skip to first unread message

Benjamin Lang

unread,
Nov 12, 2018, 7:01:30 AM11/12/18
to Axon Framework Users
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<T> extends AggregateAnnotationCommandHandler<T> {

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

   
@Override
   
protected Object resolveReturnValue(CommandMessage<?> command, Aggregate<T> 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

Allard Buijze

unread,
Nov 16, 2018, 4:29:21 AM11/16/18
to axonfr...@googlegroups.com
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

--
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.
--
Allard Buijze
CTO

E: allard...@axoniq.io
T: +31 6 34 73 99 89

Benjamin Lang

unread,
Nov 27, 2018, 4:38:48 AM11/27/18
to Axon Framework Users
Hi Allard, thanks for the response, we will try that.

Benjamin Lang

unread,
Nov 29, 2018, 7:37:55 AM11/29/18
to Axon Framework Users
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

Allard Buijze

unread,
Dec 13, 2018, 6:33:48 AM12/13/18
to axonfr...@googlegroups.com
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

Robin Custers

unread,
Apr 18, 2019, 10:22:08 AM4/18/19
to Axon Framework Users
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
To unsubscribe from this group and stop receiving emails from it, send an email to axonfr...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Allard Buijze

unread,
Apr 24, 2019, 9:13:38 AM4/24/19
to Axon Framework Users
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,

Allard Buijze
CTO

T: +31 6 34 73 99 89

Robin Custers

unread,
Apr 24, 2019, 9:35:28 AM4/24/19
to Axon Framework Users
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
Reply all
Reply to author
Forward
0 new messages