Configurers, Components, and Decorators
This page is entirely about configuring an application, before it starts. It explains the three core building blocks you interact with while doing so:
-
ApplicationConfigurer- the entry point you use to describe your application -
Component- how individual infrastructure pieces are registered -
ComponentDecorator- how registered components can be wrapped with additional behavior
|
Retrieving components once configured
Once your application is built (and started), you no longer interact with the |
Configurers
Axon Framework applications start with a configurer.
There are multiple implementations of the configurer, specific per core module of the framework.
As such, there is a MessagingConfigurer, ModellingConfigurer, and EventSourcingConfigurer.
Each of these builds on the base interface called ApplicationConfigurer, which exposes the following operations:
-
componentRegistry(Consumer<ComponentRegistry>)- Provides access to theComponentRegistry, which is the registry containing all Components, Decorators, Modules, Enhancers, and Factories. -
lifecycleRegistry(Consumer<LifecycleRegistry>)- Provides access to theLifecycleRegistry, the registry that ensures Components are started and stopped at the intended point in time. -
build()- Builds the read-only Configuration based on everything that’s configured, without starting it. -
start()- Builds and starts read-only Configuration based on everything that’s configured.
Although the componentRegistry(Consumer<ComponentRegistry>) will provide you all the access you require to register what you need, we recommend picking an appropriate implementation of the ApplicationConfigurer.
These not only provide dedicated registration methods for ease of use, they also provide sane defaults for Axon’s infrastructure components.
|
Which configurer do I pick?
Pick your starting point based on what your application needs:
Each configurer wraps the one below it, so lower-layer registration methods remain accessible (see Accessing lower-level configurers). |
MessagingConfigurer
MessagingConfigurer is the lowest-level concrete configurer, providing all the basic message support.
As such, it arguably does the most compared to the other configurers.
Some of the infra it sets are the CommandBus, EventSink, and QueryBus.
It also provides convenience methods to register command, event, and query-specific components:
import org.axonframework.messaging.commandhandling.SimpleCommandBus;
import org.axonframework.messaging.core.configuration.MessagingConfigurer;
import org.axonframework.messaging.core.unitofwork.UnitOfWorkFactory;
import org.axonframework.messaging.eventhandling.SimpleEventBus;
import org.axonframework.messaging.queryhandling.SimpleQueryBus;
class AxonApp {
public static void main(String[] args) {
MessagingConfigurer.create()
.registerCommandBus(config -> new SimpleCommandBus(
config.getComponent(UnitOfWorkFactory.class)
))
.registerEventSink(config -> new SimpleEventBus())
.registerQueryBus(config -> new SimpleQueryBus(
config.getComponent(UnitOfWorkFactory.class)
));
}
}
ModellingConfigurer
ModellingConfigurer wraps a MessagingConfigurer and adds support for entities that represent a model of your application.
As such, it forms the entry point to provide distinct Modules for said entities to maintain modularization of your application.
Next to several convenience methods, the most noteworthy unique operation is the registerEntity(EntityModule) method.
The expected EntityModule can either be state-based (StateBasedEntityModule) or event-sourced (EventSourcedEntityModule).
The latter requires the EventSourcingConfigurer.
Let’s look at an example usage of the StateBasedEntityModule:
import org.axonframework.messaging.core.MessageStream;
import org.axonframework.messaging.core.QualifiedName;
import org.axonframework.modelling.configuration.ModellingConfigurer;
import org.axonframework.modelling.configuration.StateBasedEntityModule;
import java.util.concurrent.CompletableFuture;
class AxonApp {
public static void main(String[] args) {
StateBasedEntityModule<MyId, MyEntity> myEntityModule =
StateBasedEntityModule.declarative(MyId.class, MyEntity.class)
/* Lambda to load */.loader(c -> (id, context) -> CompletableFuture.completedFuture(new MyEntity()))
/* Lambda to persist*/.persister(c -> (id, entity, context) -> CompletableFuture.completedFuture(null))
.messagingModel((config, builder) -> builder
.instanceCommandHandler(
new QualifiedName("update-my-entity"),
(command, entity, context) -> MessageStream.empty().cast()
)
.build())
.build();
ModellingConfigurer.create()
.registerEntity(myEntityModule);
}
}
EventSourcingConfigurer
The EventSourcingConfigurer further builds on the entity-support given by the ModellingConfigurer, adding defaults for event sourcing infrastructure, as well as convenience registration methods like:
-
registerEventStorageEngine(ComponentBuilder<EventStorageEngine>) -
registerEventStore(ComponentBuilder<EventStore>) -
registerTagResolver(ComponentBuilder<TagResolver>) -
registerEntity(EntityModule)
Let’s look at an example that registers an EventSourcedEntityModule instead of a StateBasedEntityModule as shown earlier:
import org.axonframework.eventsourcing.EventSourcedEntityFactory;
import org.axonframework.eventsourcing.configuration.EventSourcedEntityModule;
import org.axonframework.eventsourcing.configuration.EventSourcingConfigurer;
import org.axonframework.messaging.core.MessageStream;
import org.axonframework.messaging.core.QualifiedName;
import org.axonframework.messaging.eventstreaming.EventCriteria;
import org.axonframework.modelling.configuration.EntityMetamodelConfigurationBuilder;
class AxonApp {
public static void main(String[] args) {
EntityMetamodelConfigurationBuilder<MyEntity> metamodelBuilder =
(configuration, builder) -> builder.creationalCommandHandler(
new QualifiedName("creational-command"),
(command, context) -> MessageStream.empty().cast()
)
// Additional handlers omitted
.build();
EventSourcedEntityModule<MyId, MyEntity> myEntityModule =
EventSourcedEntityModule.declarative(MyId.class, MyEntity.class)
.messagingModel(metamodelBuilder)
.entityFactory(c -> EventSourcedEntityFactory.fromNoArgument(MyEntity::new))
.criteriaResolver(c -> (id, context) ->
EventCriteria.havingTags("myId", "value"))
.build();
EventSourcingConfigurer.create()
.registerEntity(myEntityModule);
}
}
Accessing lower-level configurers
Because each configurer wraps the one below it, some operations are only available at a specific layer.
To access these layers, every wrapping ApplicationConfigurer provides accessor methods.
Let’s look at an example of the EventSourcingConfigurer accessing the modeling and messaging layer:
import org.axonframework.eventsourcing.configuration.EventSourcedEntityModule;
import org.axonframework.eventsourcing.configuration.EventSourcingConfigurer;
import org.axonframework.eventsourcing.eventstore.inmemory.InMemoryEventStorageEngine;
import org.axonframework.messaging.commandhandling.CommandBus;
import org.axonframework.messaging.commandhandling.CommandPriorityCalculator;
import org.axonframework.messaging.commandhandling.RoutingStrategy;
import org.axonframework.messaging.commandhandling.SimpleCommandBus;
import org.axonframework.messaging.commandhandling.gateway.CommandGateway;
import org.axonframework.messaging.commandhandling.gateway.DefaultCommandGateway;
import org.axonframework.messaging.core.MessageTypeResolver;
import org.axonframework.messaging.core.unitofwork.UnitOfWorkFactory;
class AxonApp {
public static void main(String[] args) {
EventSourcingConfigurer.create()
/* top level: */ .registerEventStorageEngine(
c -> new InMemoryEventStorageEngine()
)
/* modelling level: */.modelling(mc -> mc.registerEntity(
EventSourcedEntityModule.autodetected(
MyId.class, MyEntity.class
)
))
/* messaging level: */.messaging(mc -> mc.registerCommandBus(
config -> new SimpleCommandBus(config.getComponent(UnitOfWorkFactory.class))
))
/* lowest level: */ .componentRegistry(cr -> cr.registerComponent(
CommandGateway.class,
config -> new DefaultCommandGateway(
config.getComponent(CommandBus.class),
config.getComponent(MessageTypeResolver.class),
config.getComponent(CommandPriorityCalculator.class),
config.getComponent(RoutingStrategy.class)
)
));
}
}
The componentRegistry(Consumer<ComponentRegistry>) and lifecycleRegistry(Consumer<LifecycleRegistry>) operations are available on every configurer layer and always target the same underlying registries.
Registering components
Every piece of Axon infrastructure registered through the configuration API is tracked as a Component.
A Component holds a type identifier, an optional name, and a lazy supplier that produces the actual instance on first access.
Components are registered through the ComponentRegistry using a ComponentBuilder, which is a functional interface that receives the Configuration and returns the instance.
The "main" component of a given type, the one you get back when you do not specify a name, is registered with just that type:
import org.axonframework.messaging.commandhandling.CommandBus;
import org.axonframework.messaging.commandhandling.CommandPriorityCalculator;
import org.axonframework.messaging.commandhandling.RoutingStrategy;
import org.axonframework.messaging.commandhandling.gateway.CommandGateway;
import org.axonframework.messaging.commandhandling.gateway.DefaultCommandGateway;
import org.axonframework.messaging.core.MessageTypeResolver;
import org.axonframework.messaging.core.configuration.MessagingConfigurer;
class RegisterComponentExample {
void register() {
MessagingConfigurer.create()
.componentRegistry(registry -> registry.registerComponent(
CommandGateway.class,
config -> new DefaultCommandGateway(
config.getComponent(CommandBus.class),
config.getComponent(MessageTypeResolver.class),
config.getComponent(CommandPriorityCalculator.class),
config.getComponent(RoutingStrategy.class)
)
));
}
}
The Configuration passed to the ComponentBuilder allows retrieving other components to use during construction.
In doing so, the ComponentBuilder supports lazy initialization.
The builder is only called the first time the component is requested, and the resulting instance is cached for subsequent requests.
When several components of the same type need to coexist, for example multiple EventStorageEngine instances, register each under a distinct name instead:
import org.axonframework.common.configuration.ComponentRegistry;
import org.axonframework.eventsourcing.eventstore.EventStorageEngine;
import org.axonframework.eventsourcing.eventstore.inmemory.InMemoryEventStorageEngine;
class NamedComponentsExample {
void register(ComponentRegistry registry) {
registry.registerComponent(
EventStorageEngine.class,
"primary-context",
config -> new InMemoryEventStorageEngine()
);
registry.registerComponent(
EventStorageEngine.class,
"archive-context",
config -> new InMemoryEventStorageEngine()
);
}
}
To retrieve components registered with a name, use the getComponent operation that expects you to provide the name.
Thus to retrieve the component registered in the sample above, invoke configuration.getComponent(EventStorageEngine.class, "primary-context").
Both registerComponent(Class<C>, ComponentBuilder<C>) and registerComponent(Class<C>, String, ComponentBuilder<C>)
are convenience shortcuts around the richer ComponentDefinition API.
A ComponentDefinition is built through
ComponentDefinition.ofType(Class<C>), or ComponentDefinition.ofTypeAndName(Class<C>, String) for a named component, and completed with .withBuilder(ComponentBuilder<C>), or .withInstance© when the instance already exists rather than needing to be built lazily.
Reach for ComponentDefinition directly whenever you need more than a plain builder, for example to attach startup or shutdown behavior (see Component lifecycle
below), or to register a component under a generic type through its TypeReference-based ofType/ofTypeAndName
overloads.
import org.axonframework.common.configuration.ComponentDefinition;
import org.axonframework.common.configuration.ComponentRegistry;
class ComponentDefinitionExample {
void register(ComponentRegistry registry) {
registry.registerComponent(
ComponentDefinition.ofType(ConnectionPool.class)
.withBuilder(config -> new ConnectionPool())
);
registry.registerComponent(
ComponentDefinition.ofTypeAndName(ConnectionPool.class, "archive-pool")
.withInstance(new ConnectionPool())
);
}
}
When running with the Spring Boot Starter, a component does not have to go through registerComponent at all: simply declare a @Bean of the matching type, and Axon picks it up automatically.
See Implementation details for how this works.
Component lifecycle
When a component requires startup or shutdown logic, use ComponentDefinition to attach lifecycle handlers directly to the registration.
This ensures components are started and stopped in the correct phase without requiring eager initialization:
import org.axonframework.common.configuration.ComponentDefinition;
import org.axonframework.common.configuration.ComponentRegistry;
import org.axonframework.common.lifecycle.Phase;
class ComponentLifecycleExample {
void register(ComponentRegistry registry) {
registry.registerComponent(
ComponentDefinition.ofType(ConnectionPool.class)
.withBuilder(config -> new ConnectionPool())
.onStart(Phase.INSTRUCTION_COMPONENTS, ConnectionPool::start)
.onShutdown(Phase.INSTRUCTION_COMPONENTS, ConnectionPool::shutdown)
);
}
}
The Phase constant determines the relative order in which startup and shutdown are invoked across all components.
Lower phase values start first; shutdown proceeds in reverse order.
Conditional and default registration
The registerIfNotPresent(Class<C>, ComponentBuilder<C>) method registers a component only when no component of that type has been registered yet.
Axon uses this internally to provide defaults of all important components without overriding what users might configure themselves.
Thus, the following sample will set a CommandBus if no CommandBus has already been set:
import org.axonframework.common.configuration.ComponentRegistry;
import org.axonframework.messaging.commandhandling.CommandBus;
import org.axonframework.messaging.commandhandling.SimpleCommandBus;
import org.axonframework.messaging.core.unitofwork.UnitOfWorkFactory;
class ConditionalDefaultRegistrationExample {
void register(ComponentRegistry registry) {
registry.registerIfNotPresent(
CommandBus.class,
config -> new SimpleCommandBus(config.getComponent(UnitOfWorkFactory.class))
);
}
}
A typical use case for registerIfNotPresent semantics is in a ConfigurationEnhancer, as explained in more detail here.
It’s ConfigurationEnhancer implementations that set all the defaults in Axon Framework, by means of registerIfNotPresent.
Hence, if you have components that should conditionally be added to your configuration, an enhancer with registerIfNotPresent is the way to go.
Decorating components
A ComponentDecorator wraps an existing component with additional behavior, without requiring knowledge of its concrete type.
This is the mechanism Axon uses internally to add intercepting and tracing to its infrastructure components, for example.
A decorator is registered with ComponentRegistry#registerDecorator(Class<C>, int order, ComponentDecorator<C, D>).
The decorator receives the current Configuration, the component name, and the delegate instance, and is invoked on the given order:
import org.axonframework.messaging.commandhandling.CommandBus;
import org.axonframework.messaging.core.configuration.MessagingConfigurer;
import org.axonframework.messaging.tracing.SpanFactory;
class DecoratorExample {
void register() {
MessagingConfigurer.create()
.componentRegistry(registry -> registry.registerDecorator(
CommandBus.class,
10,
(config, name, delegate) -> new TracingCommandBusDecorator(
delegate, config.getComponent(SpanFactory.class)
)
));
}
}
To decorate only a component registered under a specific name instead of every component of that type, use
registerDecorator(Class<C>, String name, int order, ComponentDecorator<C, D>) instead:
import org.axonframework.common.configuration.ComponentRegistry;
import org.axonframework.messaging.commandhandling.CommandBus;
import org.axonframework.messaging.tracing.SpanFactory;
class NamedDecoratorExample {
void register(ComponentRegistry registry) {
registry.registerDecorator(
CommandBus.class, "primary-context", 0,
(config, name, delegate) -> new TracingCommandBusDecorator(
delegate, config.getComponent(SpanFactory.class)
)
);
}
}
Both overloads are convenience shortcuts around the richer DecoratorDefinition API.
A DecoratorDefinition is built through DecoratorDefinition.forType(Class<C>), or DecoratorDefinition.forTypeAndName(Class<C>, String) to target a specific named component, and completed with .with(ComponentDecorator<C, D>).
Reach for DecoratorDefinition directly whenever you need more than a plain decorator function, for example to set an explicit order or to attach startup or shutdown behavior (see Decorator lifecycle below):
import org.axonframework.common.configuration.ComponentRegistry;
import org.axonframework.common.configuration.DecoratorDefinition;
import org.axonframework.messaging.commandhandling.CommandBus;
import org.axonframework.messaging.tracing.SpanFactory;
class DecoratorDefinitionExample {
void register(ComponentRegistry registry) {
registry.registerDecorator(
DecoratorDefinition.forType(CommandBus.class)
.with((config, name, delegate) -> new TracingCommandBusDecorator(
delegate, config.getComponent(SpanFactory.class)
))
.order(10)
);
registry.registerDecorator(
DecoratorDefinition.forTypeAndName(CommandBus.class, "primary-context")
.with((config, name, delegate) -> new TracingCommandBusDecorator(
delegate, config.getComponent(SpanFactory.class)
))
);
}
}
When running with the Spring Boot Starter, a decorator does not have to go through registerDecorator at all either: declare the DecoratorDefinition as a Spring bean, and Axon picks it up automatically.
See Customizing Axon’s configuration through Spring for an example.
Ordering decorators
The order parameter controls the sequence in which decorators are applied when several of them target the same component type (or type-and-name combination).
Decorators with a lower order value are applied first, making them the innermost wrapper, closer to the original component.
Decorators with a higher order value are applied next, wrapping the result of the lower-order decorators and ending up closer to any caller.
A decorator that depends on the behavior of another decorator must therefore have a strictly higher order value than the one it depends on.
Consider decorating a CommandBus with both a metering and a tracing decorator:
import org.axonframework.messaging.commandhandling.CommandBus;
import org.axonframework.messaging.core.configuration.MessagingConfigurer;
import org.axonframework.messaging.tracing.SpanFactory;
class OrderedDecoratorsExample {
void register() {
MessagingConfigurer.create()
.componentRegistry(registry -> registry
.registerDecorator(
CommandBus.class,
0,
(config, name, delegate) -> new MeteringCommandBusDecorator(delegate)
)
.registerDecorator(
CommandBus.class,
10,
(config, name, delegate) -> new TracingCommandBusDecorator(
delegate, config.getComponent(SpanFactory.class)
)
));
}
}
Because MeteringCommandBusDecorator has the lower order (0), it is applied directly to the raw CommandBus, making it the innermost wrapper. TracingCommandBusDecorator, with the higher order (10), wraps the metering decorator next, making it the outermost wrapper.
As a result, a dispatched command flows through
TracingCommandBusDecorator → MeteringCommandBusDecorator → the original CommandBus.
This ensures every dispatch, including the time spent inside the metering decorator, is captured within the span created by the tracing decorator.
Axon Framework’s own decorators, such as the ones adding intercepting
or tracing, each use a fixed order value defined as a constant either on the decorating class itself or on the ConfigurationEnhancer that registers it.
To place your own decorator before or after one of these, look up that constant in the framework source and choose an order value lower or higher than it, depending on where your decorator needs to sit.
These constants are typically exposed as public static final int
fields, so where available, reference them directly instead of hardcoding the numeric value.
Since Axon Framework may adjust these constants between releases as new decorators are introduced, treat the surrounding source as the source of truth rather than relying on a value you noted down previously.
Decorator lifecycle
A decorator function only wraps the delegate.
It does not automatically join the application’s startup and shutdown sequence the way a registered Component does.
This matters whenever the wrapper you introduce owns resources of its own, for example a background thread that periodically flushes metrics, an open connection to an external system, or a cache that needs to be cleared.
Without an explicit lifecycle hook, such resources are never released when the application shuts down, since the framework has no way of knowing about them.
Similar to component registration, use DecoratorDefinition (instead of the registerDecorator(Class<C>, int,
ComponentDecorator<C, D>) shortcut) when the decorator itself requires startup or shutdown logic, attaching
.onStart(int, …) and/or .onShutdown(int, …) handlers directly to the registration.
As with regular component lifecycle handlers, the phase passed to these handlers determines exactly when the decorator’s own logic runs relative to all other components and decorators being started or stopped.
The following example decorates a
CommandBus with a metering decorator that starts a scheduled metrics-flushing task on construction, and relies on
onShutdown to stop that task cleanly when the application shuts down:
import org.axonframework.common.configuration.ComponentRegistry;
import org.axonframework.common.configuration.DecoratorDefinition;
import org.axonframework.common.lifecycle.Phase;
import org.axonframework.messaging.commandhandling.CommandBus;
class DecoratorLifecycleExample {
void register(ComponentRegistry registry) {
registry.registerDecorator(
DecoratorDefinition.forType(CommandBus.class)
.with((config, name, delegate) -> new MeteringCommandBusDecorator(delegate))
.order(5)
.onShutdown(Phase.INBOUND_COMMAND_CONNECTOR, MeteringCommandBusDecorator::shutdown)
);
}
}
|
Where do components and decorators actually live?
Every operation described on this page, registering components, decorating them, or accessing a lower-level configurer, ultimately acts on one of two registries: the |
Identifying components: class, name, or type-reference
Every Component and ComponentDecorator is matched against an identifier made up of a declared type and an optional name.
Depending on what you are registering, you reach for that identifier through a Class, a Class plus a String name, or, for a component with a generic declared type, a TypeReference.
By class is the default: ComponentDefinition.ofType(MyService.class) or the registerComponent(Class<C>, ComponentBuilder<C>) shortcut identify a component purely by its raw type.
This is sufficient whenever only a single instance of that type is expected in the ComponentRegistry, which covers most infrastructure components such as the CommandBus or QueryBus.
By name adds a String alongside the class, through ComponentDefinition.ofTypeAndName(MyService.class, "myName") or registerComponent(Class<C>, String, ComponentBuilder<C>).
Use this the moment several components of the same declared type need to coexist, as shown earlier for multiple EventStorageEngine instances.
The same class-plus-name pattern applies to decorators, through DecoratorDefinition.forTypeAndName(Class<C>, String), to target a single named component rather than every component of that type.
By TypeReference replaces the Class argument whenever the declared type itself is generic, for example Repository<MyId, MyEntity>.
A plain Class<Repository> object cannot carry the <MyId, MyEntity> type arguments, since generic type information is erased at runtime.
TypeReference works around this erasure by capturing the full generic type through an anonymous subclass, so the ComponentRegistry and Configuration can distinguish a Repository<OrderId, Order> from a Repository<InvoiceId, Invoice> even though both erase to the same Repository class.
ComponentDefinition.ofType(TypeReference<C>) and ofTypeAndName(TypeReference<C>, String) register a component this way, mirroring their Class-based counterparts; Configuration#getComponent(TypeReference<C>) and its overloads (covered in more detail under retrieving components) retrieve it again with the same type safety.
Decorators do not have a TypeReference-based overload: DecoratorDefinition always matches on the raw, erased Class, so a decorator registered for Repository.class applies to every Repository, regardless of its type arguments.
The following example registers an EntityCache<MyId, MyEntity> component under its full generic type, and retrieves it again the same way:
import org.axonframework.common.TypeReference;
import org.axonframework.common.configuration.ComponentDefinition;
import org.axonframework.common.configuration.ComponentRegistry;
import org.axonframework.common.configuration.Configuration;
class TypeReferenceComponentExample {
void register(ComponentRegistry registry) {
registry.registerComponent(
ComponentDefinition.ofType(new TypeReference<EntityCache<MyId, MyEntity>>() {})
.withBuilder(config -> id -> null)
);
}
void retrieve(Configuration configuration) {
EntityCache<MyId, MyEntity> cache =
configuration.getComponent(new TypeReference<EntityCache<MyId, MyEntity>>() {});
}
}