@CommandHandler
. The @CommandHandler
annotated method will become a Command Handler for Command Messages where the command name matches fully qualified class name of the first parameter of that method. Thus, a method signature of void handle(RedeemCardCommand cmd)
annotated with @CommandHandler
, will be the Command Handler of the RedeemCardCommand
Command Messages.String commandName
value can be specified in the @CommandHandler
annotation.@TargetAggregateIdentifier
. The annotation may be placed on either the field or an accessor method (e.g. a getter) in the Command object.Routing in a distributed environmentRegardless of the type of command, as soon as you start distributing your application (through Axon Server, for example), it is recommended to specify a routing key on the command. This is the job of the@TargetAggregateIdentifier
, but in absence of a field worthy of the annotation, the@RoutingKey
annotation should be added to ensure the command can be routed.If neither annotation works for your use case, a differentRoutingStrategy
can be configured, as is explained in the Routing Strategy section.
GiftCard
Aggregate as an example, we can identify two Command Handlers on the Aggregate:IssueCardCommand
and RedeemCardCommand
, which GiftCard
handles have the following format:cardId
present in both commands is the reference to a GiftCard
instance and thus is annotated with the @TargetAggregateIdentifier
annotation. Commands that create an Aggregate instance do not need to identify the target aggregate identifier, as there is no Aggregate in existence yet. It is nonetheless recommended for consistency to annotate the Aggregate Identifier on them as well.CommandTargetResolver
. This class should return the Aggregate Identifier and expected version (if any) based on a given command.Aggregate Creation Command HandlersWhen the@CommandHandler
annotation is placed on an aggregate's constructor, the respective command will create a new instance of that aggregate and add it to the repository. Those commands do not require to target a specific aggregate instance. Therefore, those commands need neither the@TargetAggregateIdentifier
nor the@TargetAggregateVersion
annotation. Furthermore, a customCommandTargetResolver
will not be invoked for these commands.
When to handle an EventThe only state an Aggregate requires is the state it needs to make a decision. Handling an Event published by the Aggregate is thus only required if the state change the Event resembles is needed to drive future validation.
apply()
new events inside an Event Sourcing Handler method. This makes it possible for an Entity 'B' to apply an event in reaction to Entity 'A' doing something. Axon will ignore the apply()
invocation when replaying historic events upon sourcing the given Aggregate. Do note that in the scenario where Event Messages are published from an Event Sourcing Handler, the Event of the inner apply()
invocation is only published to the entities after all entities have received the first event. If more events need to be published, based on the state of an entity after applying an inner event, use apply(...).andThenApply(...)
.Reacting to other EventsAn Aggregate cannot handle events from other sources then itself. This is intentional as the Event Sourcing Handlers are used to recreate the state of the Aggregate. For this it only needs it's own events as those represent it's state changes.To make an Aggregate react on events from other Aggregate instances, Sagas or Event Handling Components should be leveraged
GiftCard
aggregate with roughly two types of command handlers:@CommandHandler
annotated constructors@CommandHandler
annotated methodsGiftCard
aggregate, whilst option 2 expects to be targeted towards an existing aggregate instance. Although this may be the default, there is the option to define a creation policy on a command handler. This can be achieved by adding the @CreationPolicy
annotation to a command handler annotated method, like so:@CreationPolicy
annotation requires stating the AggregateCreationPolicy
. This enumeration has the following options available:ALWAYS
- A creation policy of "always" will expect to instantiate the aggregate. This effectively works like a command handler annotated constructor. Without defining a return type, the aggregate identifier used during the creation will be returned. Through this approach, it is possible to return other results next to the aggregate identifier.CREATE_IF_MISSING
- A creation policy of "create if missing" can either create an aggregate or act on an existing instance.NEVER
- A creation policy of "never" will be handled on an existing aggregate instance.@CommandHandler
annotated methods. Unlike with Aggregates, there is only a single instance of a Command Handling Object, which handles all commands of the types it declares in its methods:RedeemCardCommand
should no longer be directly handled on the GiftCard
. Instead, we load the GiftCard
manually and execute the desired method on it:Repository
for the GiftCard
Aggregate, used for retrieval and storage of an Aggregate.@CommandHandler
methods are placed directly on the Aggregate, Axon will automatically know to call the Repository
to load a given instance.GiftCard
Aggregate instance, the Repository#load(String)
method is used.Aggregate#execute(Consumer)
function should be invoked to perform an operation on the Aggregate.execute
function ensure that the Aggregate life cycle is correctly started.