19

Parameter Resolvers in Axon

 5 years ago
source link: https://www.tuicool.com/articles/hit/eEvI3u2
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Axon 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 fully open-source package here . The Axon Framework consists of message-based commands, events, and queries that are supported types of messages. For each of these messages, we can define a handler (a method or 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).

EJnqMzR.png!web

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 an 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, it 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 framework users with the means of defining their own   ParameterResolverFactories .

Parameter Resolver Cheat Sheet

Figure 2 shows all ParameterResolverFactories   provided by the framework.

M7fmInI.png!web

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

  • an 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   that 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 that is used to resolve the timestamp of an event message.

    @EventHandler
    public void on(MyEvent event, @Timestamp Instant timestamp) {
        // handle event
    }
  • The   MessageIdentifierParameterResolverFactory 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 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.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK