Configuration
This page describes how to configure command handling in your Axon application.
Configuration in Axon Framework uses the ApplicationConfigurer hierarchy, with specialized configurers for different concerns:
-
MessagingConfigurer- For messaging infrastructure (command bus, query bus, event sink). -
ModellingConfigurer- For domain modeling components. -
EventSourcingConfigurer- For event sourcing and event stores.
Configuration approaches
Axon Framework currently supports two configuration approaches:
- Configuration API
-
You use the configurer classes to explicitly configure components. This gives you full control over component creation and wiring.
- Spring Boot
-
When using Spring Boot, most configuration happens automatically through annotations and bean definitions. Components are discovered and registered automatically.
Configuring event-sourced entities
Event-sourced entities are the most common pattern for stateful command handling.
-
Configuration API
-
Spring Boot
Use EventSourcingConfigurer to register entities:
import org.axonframework.eventsourcing.configuration.EventSourcingConfigurer;
import org.axonframework.modelling.configuration.EntityModule;
public class AxonConfig {
public void configureEntity() {
EventSourcingConfigurer configurer = EventSourcingConfigurer.create();
// Register an entity module
configurer.registerEntity(
EventSourcedEntityModule.autodetected(String.class, GiftCard.class).build()
);
}
}
For using the autodetected, the given entity class should use annotation.
When this is the case, this approach creates and registers:
-
An
EventSourcedRepositoryfor theGiftCardentity. -
Command handler subscriptions for all
@CommandHandlermethods. -
Event sourcing handlers for state reconstitution.
If full manual configuration is required, for example in absence of annotations, use the EventSourcedEntityModule#declarative flow instead.
Use the @EventSourced annotation on your entity class:
import org.axonframework.extension.spring.stereotype.EventSourced;
import org.axonframework.messaging.commandhandling.annotation.CommandHandler;
import org.axonframework.eventsourcing.annotation.EventSourcingHandler;
@EventSourced
public class GiftCard {
private String id;
private int remainingValue;
@CommandHandler
public static GiftCard handle(IssueCardCommand cmd, EventAppender eventAppender) {
GiftCard card = new GiftCard();
eventAppender.append(new CardIssuedEvent(cmd.cardId(), cmd.amount()));
return card;
}
@CommandHandler
public void handle(RedeemCardCommand cmd, EventAppender eventAppender) {
if (remainingValue >= cmd.amount()) {
eventAppender.append(new CardRedeemedEvent(id, cmd.amount()));
}
}
@EventSourcingHandler
private void on(CardIssuedEvent event) {
this.id = event.cardId();
this.remainingValue = event.amount();
}
@EventSourcingHandler
private void on(CardRedeemedEvent event) {
this.remainingValue -= event.amount();
}
@EntityCreator
protected GiftCard() {
// Required no-arg constructor for reconstitution
}
}
Spring Boot automatically:
-
Discovers the entity through component scanning
-
Creates an
EventSourcedRepositoryfor the entity -
Registers all
@CommandHandlermethods with the command bus
Configuring command handling components
Command handling components are classes containing one or more command handling functions. These can also be called external command handlers or command-centric handlers. 'External' and 'command-centric' refer to the fact the command handler is not contained inside an entity. Consider the examples below when configuring a command handling component:
-
Configuration API
-
Spring Boot
Use CommandHandlingModule to register handlers:
import org.axonframework.messaging.core.configuration.MessagingConfigurer;
import org.axonframework.messaging.commandhandling.configuration.CommandHandlingModule;
public class AxonConfig {
public void configureCommandHandlers() {
MessagingConfigurer.create()
.registerCommandHandlingModule(
CommandHandlingModule.named("gift-card-commands")
.commandHandlers(handlers ->
handlers.annotatedCommandHandlingComponent(
config -> new GiftCardCommandHandler(
config.getComponent(Repository.class)
)
)
)
);
}
}
The annotatedCommandHandlingComponent() method scans for @CommandHandler annotations and registers them.
If full manual configuration is required, for example in absence of annotations, use the commandHandler(QualifiedName, CommandHandler) flow instead.
Annotate your command handling component class with @Component:
import org.springframework.stereotype.Component;
import org.axonframework.messaging.commandhandling.annotation.CommandHandler;
@Component
public class GiftCardCommandHandler {
private final Repository<String, GiftCard> giftCardRepository;
public GiftCardCommandHandler(Repository<String, GiftCard> giftCardRepository) {
this.giftCardRepository = giftCardRepository;
}
@CommandHandler
public void handle(CancelCardCommand cmd,
ProcessingContext context,
EventAppender eventAppender) {
giftCardRepository.load(cmd.cardId(), context)
.thenAccept(managedCard -> {
GiftCard card = managedCard.entity();
if (card.canBeCancelled()) {
eventAppender.append(new CardCancelledEvent(cmd.cardId()));
}
});
}
}
Spring Boot automatically discovers and registers the handler.
Component registration patterns
When using the Configuration API, components are registered using ComponentBuilder functions.
Basic component registration
Register any component via the component registry:
import org.axonframework.common.configuration.ApplicationConfigurer;
import org.axonframework.common.configuration.ComponentBuilder;
public class AxonConfig {
public void registerMyService(ApplicationConfigurer configurer) {
ComponentBuilder<MyService> serviceBuilder = config -> new MyService(
config.getComponent(SomeDependency.class)
);
configurer.componentRegistry(
registry -> registry.registerComponent(MyService.class, serviceBuilder)
);
}
}
The ComponentBuilder lambda receives a Configuration that provides access to other registered components.
Component replacement
Replace default components by registering with the same type:
import org.axonframework.messaging.commandhandling.SimpleCommandBus;
import org.axonframework.messaging.core.configuration.MessagingConfigurer;
import org.axonframework.messaging.core.unitofwork.UnitOfWorkFactory;
public class AxonConfig {
public static void main(String[] args) {
MessagingConfigurer.create().registerCommandBus(
config -> new SimpleCommandBus(
config.getComponent(UnitOfWorkFactory.class),
Collections.emptyList()
)
);
}
}
The last registration wins - this replaces any previously registered CommandBus.
Component decoration
Enhance existing components using decorators:
import org.axonframework.common.configuration.ApplicationConfigurer;
import org.axonframework.common.configuration.ComponentDecorator;
import org.axonframework.messaging.commandhandling.CommandBus;
public class AxonConfig {
public void decorateCommandBus(ApplicationConfigurer configurer) {
configurer.componentRegistry(registry -> registry.registerDecorator(
CommandBus.class,
0, // Integer defining the decoration order
(config, name, commandBus) -> new LoggingCommandBus(commandBus)
));
}
}
Example decorator that adds logging:
public class LoggingCommandBus implements CommandBus {
private final CommandBus delegate;
private final Logger logger = LoggerFactory.getLogger(LoggingCommandBus.class);
public LoggingCommandBus(CommandBus delegate) {
this.delegate = delegate;
}
@Override
public CompletableFuture<CommandMessage<?>> dispatch(
CommandMessage<?> command,
ProcessingContext context) {
logger.info("Dispatching command: {}", command.commandName());
return delegate.dispatch(command, context)
.whenComplete((result, error) -> {
if (error != null) {
logger.error("Command failed: {}", command.commandName(), error);
} else {
logger.info("Command succeeded: {}", command.commandName());
}
});
}
// Delegate other methods...
}
Decorators are applied in registration order and wrap the original component.
Interceptor registration
Interceptors provide cross-cutting concerns like logging, security, or validation.
Dispatch interceptors
Invoked when a message is dispatched, before routing:
-
Configuration API
-
Spring Boot
Register interceptors explicitly:
public class AxonConfig {
private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static void main(String[] args) {
MessagingConfigurer.create().registerCommandDispatchInterceptor(
config -> (command, context, interceptorChain) -> {
logger.info("Dispatching: {}", command.type().name());
return interceptorChain.proceed(command, context);
}
);
}
}
Define interceptors as beans:
@Configuration
public class InterceptorConfig {
private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Bean
public MessageDispatchInterceptor<CommandMessage> loggingInterceptor() {
return (command, context, interceptorChain) -> {
logger.info("Dispatching: {}", command.type().name());
return interceptorChain.proceed(command, context);
};
}
}
Spring Boot automatically registers interceptor beans with the appropriate bus.
Handler interceptors
Invoked before the actual handler, after routing:
-
Configuration API
-
Spring Boot
Register handler interceptors:
public class AxonConfig {
private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static void main(String[] args) {
MessagingConfigurer.create().registerCommandHandlerInterceptor(
config -> (command, context, interceptorChain) -> {
checkPermissions(command);
return interceptorChain.proceed(command, context);
}
);
}
}
Define handler interceptors as beans:
@Configuration
public class InterceptorConfig {
@Bean
public MessageHandlerInterceptor<? super CommandMessage> securityInterceptor() {
return (command, context, interceptorChain) -> {
checkPermissions(command);
return interceptorChain.proceed(command, context);
};
}
}
See Message intercepting for detailed information on interceptors.
Configuration builder pattern
When using the Configuration API, you typically chain multiple configurers:
import org.axonframework.eventsourcing.configuration.EventSourcingConfigurer;
import org.axonframework.messaging.commandhandling.configuration.CommandHandlingModule;
public class AxonConfig {
public AxonConfiguration buildConfiguration() {
return EventSourcingConfigurer.create()
// Register entities
.registerEntity(EventSourcedEntityModule.autodetected(
String.class, GiftCard.class)
)
// Register command handlers
.registerCommandHandlingModule(
CommandHandlingModule.named("gift-card-commands").commandHandlers(
handlers -> handlers.annotatedCommandHandlingComponent(
config -> new GiftCardCommandHandler(
config.getComponent(Repository.class)
)
)
)
)
// Register interceptors
.messaging(messagingConfigurer -> messagingConfigurer.registerCommandHandlerInterceptor(
config -> (command, context, chain) -> {
logger.info("Handling: {}", command.type().name());
return chain.proceed(command, context);
}
))
// Build and start
.start();
}
}
The configurer hierarchy allows you to use specialized methods while maintaining a fluent API.