Axon is is an open source Java framework for building systems in CQRS (Command Query Responsibility Segregation) , DDD (Domain Driven Design) and Event Sourcing manner. Axon provides a high level of location transparency which gives the opportunity to easily split the system into several Microservices. You can download the full open source package here. The Axon Framework is message based - commands, events and queries are supported types of messages. For each of these messages, we can define a handler (a method or a constructor) to handle it. In some cases, the message itself has enough information to be handled, but in many others, we have a dependency on other components and/or variables (message metadata such as correlation information would be a good example). Axon provides a really powerful mechanism to inject these dependencies into message handlers - Parameter Resolvers. This is the story about them. At the end of this article, you can find a cheat sheet of all resolvers provided by the Axon Framework.

Anatomy

There are two important components that support this mechanism: the ParameterResolver and the ParameterResolverFactory (see Figure 1).

Parameter Resolvers in Axon

Figure 1 - ParameterResolverFactory and ParameterResolver interfaces

The ParameterResolverFactory is used to create instances of the ParameterResolvers. Axon does an inspection of messaging handling members (such as command handlers, event handlers, and query handlers) using reflection. For each of these handlers, we iterate through its parameter list and try to find (using the ParameterResolverFactory) the corresponding ParameterResolver which can resolve the given parameter. Just before handling a concrete message we resolve the message handler parameters using previously created resolvers. This is how the injection of specific fields and components into message handlers is done in Axon.

Wiring

There is an implementation of the ParameterResolverFactory which is called MultiParameterResolverFactory. It is used to hold an array of ParameterResolverFactories and to delegate the creation of a specific ParameterResolver to them.

Service Loader is out-of-the-box Java API which offers a specific form of Inversion of Control. It is designed to locate implementation classes of an interface on the classpath. This setup allows us to discover which available implementations of an interface are available on the classpath at runtime, and thus paves the way for modules designed around a clean separation between an API module and multiple implementation modules. It’s implemented as a file located in the META-INF/services folder of a JAR. The name of the file is the fully qualified name of the interface, while its content is a slit of qualified names of available implementations.

The ParameterResolverFactory can be configured using the Configurer. The ClasspathParameterResolverFactory (not an implementation of ParameterResolverFactory) is a default way of providing ParameterResolverFactories to the framework using the Service Loader mechanism to load all available implementations. All implementations are wrapped in the MultiParameterResolverFactory. This is also an extension point providing Axon Framework users with the means of defining their own ParameterResolverFactories.

Parameter Resolver Cheat Sheet

Figure 2 shows all ParameterResolverFactories provided by the framework.

Parameter Resolvers in Axon

Figure 2 - Parameter Resolver Factories provided by Axon Framework

The DefaultParameterResolverFactory is used to create:

  • a PayloadParameterResolver: resolves the payload of a message
  • a MessageParameterResolver: resolves the message as a whole
  • a MetaDataParameterResolver: resolves the key-value map of metadata values attached to a message
  • a AnnotatedMetaDataParameterResolver: resolves specific metadata value based on the provided key

Certain parameters are annotated, hence the AbstractAnnotatedParameterResolverFactory. Corresponding factories are:

  • The SequenceNumberParameterResolverFactory, which creates a SequenceNumberParameterResolver which is used to resolve sequence number of domain events (events that are published by an aggregate)
@EventSourcingHandler 
public void on(MyEvent event, @SequenceNumber long sequenceNumber) {
// handle domain event
}
  • The TimestampParameterResolverFactory, which creates a TimestampParameterResolver which is used to resolve the timestamp of an event message.
@EventHandler 
public void on(MyEvent event, @Timestamp Instant timestamp) {
   // handle event
}
  • The MessageIdentifierParameterResolverFactory, which creates a MessageIdentifierParameterResolver used to resolve the identifier of a message (command, event, query, etc.).
@MessageHandler 
public void handle(Message message, @MessageIdentifier String messageIdentifier) {
   // handle message
}
  • The ConcludesBatchParameterResolverFactory, which creates a ConcludesBatchParameterResolver used to resolve whether the given event is the last one in the batch of events being processed.
@EventHandler
public void on(MyEvent event, @ConcludesBatch Boolean concludesBatch) {
   // handle event
}

Besides the aforementioned resolvers, there are many more (shown in Figure 2) which, as they are self-explanatory, will not be treated in detail here. Of course, we can always implement our own Parameter Resolvers, if necessary.

One example could be to have query model entity ready during event processing. Think of the case where you have all kinds of User events handled by event-handling components, and for most of them you’d need a User entity. We could do it by querying the UserRepository in those event handlers which is not important to our business case and just pollutes the logic. The other approach could be to create a UserParameterResolver which would query the UserRepository when User event comes along and have a User ready to be injected as a parameter to our handler.

Conclusion

While most of the resources needed to handle a certain message can be obtained by different means, Parameter Resolvers provide an elegant way of doing so. You don’t have to write all of the boiler-plate code to extract certain metadata values, to get the information whether your event handler is invoked for replaying purposes or to get sequence number of your events, you just need to declare what you need as a parameter in your message handler and (if available) Parameter Resolver will resolve the value for you. In the end, all you have to worry about is your business case.

For more technical information check the reference guide.

Go ahead and download Axon at https://axoniq.io/download

Subscribe to blog notifications