Spring: Upcaster order

Hi everyone,

I stumbled upon a problem regarding the order of event upcasters in a Spring configured Axon application.

As far as I understood using Spring @Order annotation can be used to configure the event upcasters’ order. To quote @allardbz (Source: EventUpcaster for EventStore using Axon Server - #3 by allardbz):

if you’re on Spring Boot, you can just define your upcasters in your application context. Use Spring’s @Order to make sure they are put into a chain in the right order.

This seems not to work as expected as org.axonframework.spring.config.SpringAxonAutoConfigurer.registerEventUpcasters(Configurer) uses org.springframework.beans.factory.ListableBeanFactory.getBeanNamesForType(Class<?>) to get the bean names of type EventUpcaster. This method does not take the @Order annotation into account. To quote the Javadoc:

Bean names returned by this method should always return bean names in the order of definition in the backend configuration, as far as possible.

To me it looks like that it’s not possible to use @Order to configure the event upcaster precedence. Do I miss anything or do I have another chance to register the upcasters in a certain order?

Thanks!

Cheers
Oliver

I would wager that the order still works. We share this concept during the training without any different stances from the community (up till now, of course). I can’t see any test case in the framework on the matter, though, so you might just have found something.

From your statement, I am, however, uncertain whether you have tested this on your environment. Would you be able to share with us whether you have actually tried it out, @Oliver_Libutzki?
If so, this would warrant an issue on the issue tracker to investigate it further on our end.

In the meantime, you can circumvent the usage of the @Order annotation by providing an EventUpcasterChain bean in your Application Context. The order you insert the upcasters in the EventUpcasterChain will be the order in which they’re invoked. I hope this clarifies your options!

I created an issue and provided a reproducable example: Spring: Upcasters are not executed in the correct order · Issue #1810 · AxonFramework/AxonFramework · GitHub

We tried this approach, but it has some limitations. The EventUpcasterChain itself is a EventUpcaster which means that it’s just one EventUpcaster in the EventUpcasterChain which is provided by Axon. That means that we must be careful not to put any other EventUpcaster into the Spring context, because otherwise it’s part of Axon’s EventUpcasterChain and our custom EventUpcasterChain.

That being said, we cannot make use of the @Order annotation to get the event upcasters into the right order.

In our use case it would be beneficial to replace Axon’s EventUpcasterChain, see org.axonframework.config.DefaultConfigurer.upcasterChain. Then we would be able to provide our own implementation which uses Spring’s autowiring facilities which are capable of injecting the beans respecting the @Order annotation.

Cheers
Oliver

2 Likes

Thanks for the thorough response, as always. :slight_smile:

Taking a look at your sample right away. Will keep everybody posted on the progress under the issue you’ve constructed for it.

Got your point. This could indeed become problematic if a mix of chains and upcasters is present in the Application Context. I think this warrants an issue on it’s own, as I wager a fix should be rather straightforward.

So, a type of configureUpcasterChain or registerUpcasterChain is what you’re looking for? Although true, I’d feel Axon should be able to deal with the EventUpcasterChain present in the Application Context, actually. This would form a more friendlier usages for everybody.

On a related note, the Configurer#registerEventUpcaster(Function<Configuration, EventUpcaster>) could be used as well. The order of invocation on that method decides the order of the EventUpcaster instances within the chain. Granted, that wouldn’t be using to much of Spring’s magic, but it could serve as a workaround for the time being. Next to ensuring a single EventUpcasterChain bean is present in the application context, of course.

That’s the approach/workaorund I chose.

Configurer#registerEventUpcaster(Function<Configuration, EventUpcaster>) is invoked by org.axonframework.spring.config.SpringAxonAutoConfigurer which collects all beans of type EventUpcaster. Unfortunately SpringAxonAutoConfigurer is not optimized for customization with its single registerBeanDefinitions method and a couple of private methods. Therefore for the time being I decided to copy the whole class and customized de.bmiag.mars.axon.client.MarsSpringAxonAutoConfigurer.registerEventUpcasters(Configurer):

private void registerEventUpcasters( final Configurer configurer ) {
	Arrays.stream( beanFactory.getBeanNamesForType( EventUpcasterChain.class ) )
			.forEach( name -> configurer.registerEventUpcaster( c -> getBean( name, c ) ) );
}

The EventUpcasterChain is registered in the Spring context and it expects a List of EventUpcasters. This list is provided (and ordered) by Spring, respecting the @Order annotation.

Copying the whole class is a dirty hack for sure, but it works for the moment until you can provide a better solution.

Oliver

I am happy you have a workaround that does the job right now.
But I agree, it is not ideal.

I’ve tested the sample you provided and came to the same conclusion, by the way.
I just constructed a fix for this for the framework, which should become part of release 4.5.2.
If you’re interested, you can find the pull request here.

1 Like

Thanks for fixing this that quickly, @Steven_van_Beelen!

I added a little comment in the pull request, but it looks good to me.

1 Like