Modules

A Module groups a set of components under a private ComponentRegistry that has a parent. The parent can be the root registry of the application or the registry of another module. This hierarchy provides two guarantees:

  1. The module can read components from its parent.

  2. The parent cannot read components from the module.

This one-way visibility enforces encapsulation: a module’s internal components remain isolated from the rest of the application. Only components registered on the root configuration or a shared ancestor are accessible from multiple places.

Without modules, every component is registered in the same flat namespace. This makes it easy for unrelated parts of the application to depend on each other’s internals. Modules enforce the principle that message handlers should not be aware of, or make assumptions about, other components: they handle messages and emit results, nothing more.

In other words, this moves the concept of location transparency to Axon’s configuration API.

Axon Framework uses this principle to wrap entities and any type of message handling component. A CommandHandlingModule, for example, can define its own internal CommandBus variant or EventSink without exposing these to the outside. Similarly, any Event Processor you register is automatically its own Module.

Registering a module

Modules are registered on the ComponentRegistry:

import org.axonframework.messaging.core.configuration.MessagingConfigurer;

class RegisteringModuleExample {

    void register() {
        MessagingConfigurer.create()
                           .componentRegistry(registry -> registry.registerModule(
                                   MyModule.named("integration")
                                           // module configuration omitted
                           ));
    }
}

Higher-level configurers also provide convenience methods:

import org.axonframework.messaging.commandhandling.configuration.CommandHandlingModule;
import org.axonframework.modelling.configuration.ModellingConfigurer;

class HigherLevelConvenienceExample {

    void register() {
        ModellingConfigurer.create()
                           .registerCommandHandlingModule(
                                   CommandHandlingModule.named("orders")
                                                        .commandHandlers()
                                                        // module configuration omitted
                           );
    }
}

Built-in modules

Axon Framework provides several built-in Module types out of the box: CommandHandlingModule, QueryHandlingModule, and EntityModule.

CommandHandlingModule

Encapsulates a group of command handlers. All command handling components (handlers, interceptors, repositories) are private to the module. The module can still read shared infrastructure components like the CommandBus or EventSink from its parent.

import org.axonframework.messaging.commandhandling.configuration.CommandHandlingModule;
import org.axonframework.messaging.core.configuration.MessagingConfigurer;

class CommandHandlingModuleExample {

    void register() {
        MessagingConfigurer.create()
                           .registerCommandHandlingModule(
                                   CommandHandlingModule.named("orders")
                                                        .commandHandlers()
                                                        // register handlers and other module-specific components
                           );
    }
}

QueryHandlingModule

Encapsulates a group of query handlers in the same way CommandHandlingModule encapsulates command handlers.

import org.axonframework.messaging.core.configuration.MessagingConfigurer;
import org.axonframework.messaging.queryhandling.configuration.QueryHandlingModule;

class QueryHandlingModuleExample {

    void register() {
        MessagingConfigurer.create()
                           .registerQueryHandlingModule(
                                   QueryHandlingModule.named("projections")
                                                      .queryHandlers()
                                                      // register handlers and other module-specific components
                           );
    }
}

EntityModule

Registers an entity with the nearest parent StateManager, making it available for lookup by identifier. An EntityModule can be state-based (StateBasedEntityModule) or event-sourced (EventSourcedEntityModule); see ModellingConfigurer and EventSourcingConfigurer for the full registration walkthrough of each:

import org.axonframework.eventsourcing.configuration.EventSourcedEntityModule;
import org.axonframework.eventsourcing.configuration.EventSourcingConfigurer;

class EntityModuleExample {

    void register() {
        EventSourcingConfigurer.create()
                               .registerEntity(
                                       EventSourcedEntityModule.autodetected(String.class, GiftCard.class)
                               );
    }
}

Accessing module configurations

After the application is built, getModuleConfiguration(String) and getModuleConfigurations() (see retrieving components) give access to the Configuration instances produced by registered modules. This is useful when inspecting what was registered, for example in tests or monitoring code:

import org.axonframework.common.configuration.AxonConfiguration;
import org.axonframework.common.configuration.Configuration;
import org.axonframework.eventsourcing.configuration.EventSourcingConfigurer;

import java.util.List;
import java.util.Optional;

class AccessingModuleConfigurationsExample {

    void inspect() {
        AxonConfiguration configuration = EventSourcingConfigurer.create()
                                                                  // registration omitted
                                                                  .build();

        // get the configuration of a specific named module
        Optional<Configuration> ordersConfig = configuration.getModuleConfiguration("orders");

        // get all module configurations
        List<Configuration> allModules = configuration.getModuleConfigurations();
    }
}

Components inside the module are accessible through the module’s own Configuration. Components from the root remain accessible through the root Configuration.

Attempting to retrieve a module-private component from the root Configuration (or from a sibling module) will throw a ComponentNotFoundException. This is intentional: it enforces the encapsulation boundary that modules provide.

Nested modules

A Module can itself register nested modules. The same visibility rules apply: a nested module can see components from its parent module and from the root, but neither the parent module nor the root can see the nested module’s private components.

import org.axonframework.messaging.core.configuration.MessagingConfigurer;

class NestedModuleExample {

    void register() {
        MessagingConfigurer.create()
                           .componentRegistry(registry -> registry.registerModule(
                                   OuterModule.named("outer")
                                              // OuterModule can register its own nested modules internally
                           ));
    }
}