Welcome to Axon Framework 102, where we will be deep diving into many interesting challenges you will encounter when working with Axon Framework. We will be diving into asynchronous projections and letting the front-end know new data. We will take a good look at handling CRUD interfaces (which we cannot always avoid) and all kinds of other good topics such as testing for missing event handlers. This blog post will dive into using a great feature of Axon Framework to your advantage: Metadata!
About the series
I have been working with Axon Framework for two years now. I have written my thesis about strangling a remaining monolith at the Port of Rotterdam Authority and I am currently doing exactly that with Axon Framework. We have seen interesting challenges, such as a great number of events, long replays, and privacy regulations. I believe those challenges to be relevant to all people using Axon Framework, or maybe even all people using Event-Sourcing! This is why I’m sharing these challenges and possible solutions with you to be inspired to make a solution just as good or even better. I have already shared the following blog posts with you:
This blog series will require some base experience with Axon Framework. If you have no experience with it yet, it is a very cool framework and you will probably enjoy learning and using it. So get your hands dirty and come back in a bit. This blog series will be waiting for you.
Metadata in Axon Framework
All commands, queries, and events in Axon Framework are messages that can have metadata attached to them. It is a map structure where you can add additional data about the event that is not part of your domain model. Authentication and audibility can be one of them; it’s great to know which user executed an action on your system, but it might be irrelevant for your domain. You could add the username to the command and the event, but this pollutes your domain model. In addition, it is not very DRY since you will have to repeat this for every command and event.
Metadata can help you achieve these goals while not repeating yourself or polluting your domain model. When dispatching a command, or applying an event, you can provide a map of the metadata you want to pass along with it. This is done in the next example, where my profile is created by a user named “hansklok”.
This data can later be referenced in all command- and event handlers (both in and outside of the aggregate) by defining MetaData as a parameter. Axon will automatically revolve the Metadata for you when you require it in your handlers. The following event handler logs the username present in the MetaData:
This example, however basic, demonstrates how useful metadata can be, especially when you have certain data which should be added to all events.
When to use
MetaData should be used for data that is not important to your domain model. I recommend using MetaData only for types of data that should be present for all events. For example, this can be the users’ name for auditing reasons, or the command name that was the precursor to the event to make debugging easier. On my current project for the Rotterdam Port Authority I have used metadata for the following data:
- Storing the username
- Storing the command name
- Storing the command payload
- Some identifying information of the entity in the aggregate
Storing this data along with the event has made troubleshooting and debugging the application much easier. However, we want to make this as low-maintenance as possible! We don’t want other developers to constantly think about all the MetaData they have to attach to their commands and events, right? This is why we can write Correlation Data Providers, which add the MetaData automatically and validate the needed properties.
Correlation Data Providers
Correlation Data Providers will provide MetaData properties based on the command message when dispatching and are managed by Axon to run fo for you. By letting Axon run it for you with every command there is no need to programmatically add MetaData to every event, which in turn reduces the chance of making a mistake greatly! Take a look at the following CorrelationDataProvider:
This Correlation Data Provider adds the Command’s name and payload to the event’s MetaData. It also copies over the username Now, if we want to see the cause of an event, all we have to do is look in the Metadata column of our event store. We will never have to think about adding those metadata properties ever again since it is automatically done for us. Axon has some other great examples on this, and even provides some auto-configured out-of-the-box!
It can always be easier
As developers we always want our lives to be easier, and most of us want this to as easily readable as possible. So, knowing that, which of the following event handlers do you prefer?
They all function the exact same way with the same result but use a different method. The first injects the entire MetaData object, of which you can then get anything. The second one uses the MetaDataValue annotation to let Axon Framework fetch us a specific key out of the MetaData. The last one uses a custom annotation and ParameterResolver to resolve a username.
Personally, I prefer the last one with the specific annotation. It’s immediately clear that we are using the username while handling this event, instead of the entire MetaData. The event handler also does not need to know the String constant “username” and we can easily find the places where we use the annotation, and thus the username. By default, Axon Framework supplies multiple annotations to handle events with ease. I have created a little overview of them for you:
|@Timestamp||Injects the timestamp of the event message as an Instant|
|@SequenceNumber||Injects the global event sequence number as a Long|
|@MessageIdentifier||Injects the id of the message as a String (this is usually a UUID)|
|@SourceId||Injects the aggregate identifier of the event as a String|
|@MetaDataValue||Injects the MetaData value you want as the type you want. Note that the types need to be matching|
Writing your own ParameterResolver
So how do we write a ParameterResolver so we can use the annotation? First, we need to define an annotation that it preserved at runtime, which looks like this in Kotlin:
Then, we write a ParameterResolver to resolve it out of the message. In our case it looks like this:
In the ParameterResolver we can use any attribute of the message of application we want. Say you have a file in your classpath and want to inject that value into your event handlers, you can! It’s up to you whether you should.
Last but not least we need to create a ParameterResolverFactory and register it with Axon Framework. The job of this Factory is to return an instance of our ParameterResolver when a matching method parameter is encountered. In our case we want it to match with an annotation, like this:
We can of course expand this when statement when we want additional annotations in our application. We can also match the parameter on its’ class. Note that Axon Framework allows only one custom factory, in which you can match as many parameters as you want.
Now we are ready to register the resolver with Axon Framework. This is done through a META-INF file containing the FQDN of the class you want to register. This file should be present in the META-INF/services classpath directory with the name “org.axonframework.messaging.annotation.ParameterResolverFactory”. Axon Framework scans this file and registers our factory on boot. Now we are ready to use our custom annotation.
It’s great we can use this MetaData, but to be really useful we want it to be always present. So let’s validate that! We can write a dispatch or handler interceptor to validate the MetaData. I prefer the dispatch interceptor since it is also under my control and it has not invoked the aggregate yet when validating, presenting the least strain on my application. Take a look at the Interceptor:
After Spring created the interceptor it registers itself to the command bus. For every outgoing command message, it calls the handle function. You can also modify the messages in the process. In our case, we want to throw an exception when the company of the invoker is not present in the MetaData. Usually, this should not happen in production since you have always through of adding it, but for testing and validation purposes during testing this is a great approach.
You can see a demo of this validation by starting the demo application here. It also contains all code included earlier in this blog post to give you a great reference to start from. You can always ask questions either here, on github, or using an old-school e-mail.
Metadata is a great way to add data to your events that is related to non-functional requirements, such as keeping the invokers’ username for auditing reasons. We can make our by registering DataCorrelationProviders, custom annotations to inject the MetaData where we need it and we can validate that the MetaData is always present. This can be of great help during our Axon Framework journey!