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 EventSourcedRepository for the GiftCard entity.

  • Command handler subscriptions for all @CommandHandler methods.

  • 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 EventSourcedRepository for the entity

  • Registers all @CommandHandler methods 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.