EventProcessingConfigurer
is used. The EventProcessingConfigurer
is part of Axon's Configuration API, dedicated to configuring Event Processors.org.axonframework.example.eventhandling.MyHandler
org.axonframework.example.eventhandling.MyOtherHandler
org.axonframework.example.eventhandling.module.ModuleHandler
org.axonframework.example.eventhandling
with two handlers called MyHandler
and MyOtherHandler
org.axonframework.example.eventhandling.module
with the single handler ModuleHandler
ProcessingGroup
annotation. This annotation resembles the Processing Group level discussed in the introduction.ProcessingGroup
annotation requires the insertion of a name and can only be set on the class. Let us adjust the previous sample by using this annotation instead of the package names for grouping handlers:ProcessingGroup
annotation as depicted, we again construct two processors:my-handlers
with two handlers called MyHandler
and MyOtherHandler
module-handlers
with the single handler ModuleHandler
EventProcessingConfigurer
exposes:byDefaultAssignTo(String)
- defines the default Processing Group name to assign an event handler to. It will only be taken into account if there are no more specifics rules and if the ProcessingGroup
annotation is not present.byDefaultAssignHandlerInstancesTo(Function<Object, String>)
- defines a lambda invoked to assign an event handling instance to a desired Processing Group by returning that group's name. It will only be taken into account if there are no more specifics rules and if the ProcessingGroup
annotation is not present.byDefaultAssignHandlerTypesTo(Function<Class<?>, String>)
- defines a lambda invoked to assign an event handler type to a desired Processing Group by returning that group's name. It will only be taken into account if there are no more specifics rules and if the ProcessingGroup
annotation is not present.assignHandlerInstancesMatching(String, Predicate<Object>)
- assigns event handlers to the given Processing Group name based on a predicate ingesting an event handling instance. The operation uses a natural priority of zero. If an instance matches several criteria, the outcome is undefined.assignHandlerTypesMatching(String, Predicate<Class<?>>)
- assigns event handlers to the given Processing Group name based on a predicate ingesting an event handler type. The operation uses a natural priority of zero. If an instance matches several criteria, the outcome is undefined.assignHandlerInstancesMatching(String, int, Predicate<Object>)
- assigns event handlers to the given Processing Group name based on a predicate ingesting an event handling instance. Uses the given priority to decide on rule-ordering. The higher the priority value, the more important the rule is. If an instance matches several criteria, the outcome is undefined.assignHandlerTypesMatching(String, int, Predicate<Class<?>>)
- assigns event handlers to the given Processing Group name based on a predicate ingesting an event handler type. Uses the given priority to decide on rule-ordering. The higher the priority, the more important the rule is. If an instance matches several criteria, the outcome is undefined.assignProcessingGroup(String, String)
- defines a given Processing Group name that belongs to the given Event Processor's name.assignProcessingGroup(Function<String, String>)
- defines a lambda invoked to assign a Processing Group name to the desired Event Processor by returning that processor's name.@Order
annotation. This annotation is placed on the event handler class name, containing an integer
value to specify the ordering.Ordering ConsiderationsAlthough we can place an order among event handlers within an Event Processor, separation of event handlers is recommended.Placing an overall ordering on event handlers means those components are inclined to interact with one another, introducing a form of coupling. Due to this, the event handling process will become complex to manage (e.g., for new team members). Furthermore, embracing an ordering approach might lead to place all event handlers in a global ordering, decreasing processing speeds in general.In all, you are free to use an ordering, but we recommend using it sparingly.
ListenerInvocationErrorHandler
. By default, these exceptions are logged (with the LoggingErrorHandler
implementation), and processing continues with the next handler or message.ListenerInvocationErrorHandler
used by each processing group can be customized. Furthermore, we can configure the error handling behavior per processing group:ErrorHandler
. The default error handler is the PropagatingErrorHandler
, which will rethrow any exceptions it catches.ErrorHandler
for all Event Processors or an ErrorHandler
for specific processors:ErrorHandler
's single method needs to be implemented:ErrorContext
object, you can decide to ignore the error, schedule retries, perform dead-letter-queue delivery, or rethrow the exception.EventProcessingConfigurer
provides access to a lot of configurable components for Event Processors. Sometimes it is easier or preferable to provide an entire function to construct an Event Processor, however. To that end, we can configure a custom EventProcessorBuilder
:EventProcessorBuilder
functional interface provides the event processor's name, the Configuration
and the EventHandlerInvoker
, and requires returning an EventProcessor
instance. Note that any Axon component that an Event Processor requires (e.g., an EventStore
) is retrievable from the Configuration
.EventProcessingConfigurer
provides two methods to configure an EventProcessorBuilder
:registerEventProcessorFactory(EventProcessorBuilder)
- allows you to define a default factory method that creates event processors for which no explicit factories are definedregisterEventProcessor(String, EventProcessorBuilder)
- defines the factory method to use to create a processor with given name
MessageHandlerInterceptor
is required to deal with an EventMessage
. Differently put, an EventHandlerInterceptor can be registered to Event Processors.EventProcessingConfigurer
provides two methods to configure MessageHandlerInterceptor
instances:registerDefaultHandlerInterceptor(BiFunction<Configuration, String, MessageHandlerInterceptor<? super EventMessage<?>>>)
- registers a default MessageHandlerInterceptor
that will be configured on every Event Processor instanceregisterHandlerInterceptor(String, Function<Configuration, MessageHandlerInterceptor<? super EventMessage<?>>>)
- registers a MessageHandlerInterceptor
that will be configured for the Event Processor matching the given String
EventProcessingConfigurer
provides two approaches towards configuring a MessageMonitor
:registerMessageMonitor(String, Function<Configuration, MessageMonitor<Message<?>>>)
- registers the given MessageMonitor
to the Event Processor matching the given String
registerMessageMonitorFactory(String, MessageMonitorFactory)
- registers the given MessageMonitorFactory
to construct a MessageMonitor
for the Event Processor matching the given String
MessageMonitorFactory
provides a more fine-grained approach, used throughout the Configuration API, to construct a MessageMonitor
:Configuration
to retrieve the required dependencies to construct the MessageMonitor
. The type and name reflect which infrastructure component the factory constructs a monitor for. Whenever you use the MessageMonitorFactory
to construct a MessageMonitor
for an Event Processor, the factory expects the componentType
to be an EventProcessor
implementation. The componentName
, on the other hand, would resemble the name of the Event Processor.TransactionManager
. Axon uses the TransactionManager
to attach a transaction to every Unit of Work. Within a Spring environment, the TransactionManager
defaults to a SpringTransactionManager
, which uses Spring's PlatformTransactionManager
under the hood. In non Spring environments, it would be wise to build a TransactionManager
implement if transaction management is required, of course. Such an implementation only requires the definition of the TransactionManager#startTransaction()
method. To adjust the transaction manager for an Event Processor, the registerTransactionManager(String, Function<Configuration, TransactionManager>)
on the EventProcessingConfigurer
should be used.RollbackConfiguration
per Event Processor. It is the RollbackConfiguration
that decide when a Unit of Work should rollback the transaction. The default RollbackConfiguration
is to rollback on any type of Throwable
; the Unit of Work page describes the other options you can choose. To adjust the default behaviour, the registerRollbackConfiguration(String, Function<Configuration, RollbackConfiguration>)
function should be invoked on the EventProcessingConfigurer
.