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:
-
The module can read components from its parent.
-
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 |
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
));
}
}