Message Migration
Axon Framework 5 introduces a more explicit way of defining and handling messages. This path covers the message-model migration from Axon Framework 4.x annotations to the new message-centric approach in AF5.
Message-specific annotations
In Axon Framework 4, the type of a message was often implicitly determined by its class or by how it was dispatched. AF5 introduces dedicated annotations to explicitly define the nature of your message objects.
The following table summarizes the primary annotation changes:
| AF4 Annotation | AF5 Replacement | Reasoning |
|---|---|---|
|
|
AF5 moves from the "Aggregate" terminology towards a more general "Entity" model. This aligns with the Dynamic Consistency Boundary (DCB) principles where boundaries are more fluid. See the architecture principles for more details. |
|
|
Consolidation of message metadata. The version is now part of the |
|
|
Similar to revision, routing information is now consolidated within the |
Explicit message types
One of the most significant changes in AF5 is the introduction of MessageType, which introduces the QualifiedName and decouples the logical name and version of a message from its implementing class.
To embrace this, you should use the @Command, @Event, and @Query annotations on your message classes.
// AF4
@Revision("1.0")
public class MyEvent {
}
// AF5
@Event(name = "MyEvent", version = "1.0")
public class MyEvent {
}
By using these annotations, you provide Axon with the necessary information to resolve the MessageType without relying on fully qualified class names, which simplifies refactoring and cross-service communication.
|
Setting the As a first migration step it is recommended to keep the default behavior to align with the previous implicit naming strategy. |
If you don’t specify the annotation parameters, Axon uses the following default behavior:
-
namespace: Defaults to the package name of the message class. -
name: Defaults to the simple class name. -
version: Defaults to0.0.1.
For more details on how message types are resolved and used, see the Message type resolution section in the Anatomy of a Message guide.
|
Message versioning
With Axon Framework 5, the versioning strategy for messages has evolved.
Instead of using the If you used |
Routing and identification
Target entity identification
When sending a command to an entity (formerly aggregate), you previously used @TargetAggregateIdentifier.
In AF5, this is replaced by @TargetEntityId. This shift is part of the broader move towards Dynamic Consistency Boundaries. See the architecture principles for more context.
// AF4
public class MarkOrderAsShippedCommand {
@TargetAggregateIdentifier
private String orderId;
}
// AF5
public class MarkOrderAsShippedCommand {
@TargetEntityId
private String orderId;
}
Command routing
For commands that require a specific routing key (for example for consistent hashing in a distributed command bus), the @RoutingKey annotation is replaced by the routingKey field in the @Command annotation.
// AF4
public class MyCommand {
@RoutingKey
private String someKey;
}
// AF5
@Command(routingKey = "someKey")
public class MyCommand {
private String someKey;
}
Note that in AF5, the routingKey property in the annotation refers to the name of the property (field or method) in the class that should be used for routing.
If a method name is provided, it can be the actual method name (getCardId) or the property name (cardId).
For more details, see the Configuring routing keys with @Command section in the Command Dispatching guide.
Event tagging for aggregate identification
In Axon Framework 4, events were implicitly associated with an aggregate through the aggregate identifier and the aggregate type, which were stored alongside the event in the event store. Axon Framework 5 moves towards a more flexible tagging system.
When migrating events that were previously associated with an aggregate, you must ensure that the event’s aggregate identifier field is annotated with @EventTag.
The key for this tag should reflect the aggregate’s type (typically the simple class name of the aggregate).
This ensures compatibility with an aggregate-based storage solution, wherein events are stored with an aggregate type column.
import org.axonframework.eventsourcing.annotation.EventTag;
// AF4
public class OrderCreatedEvent {
private String orderId;
// ...
}
// AF5
public class OrderCreatedEvent {
@EventTag(key = "Order") // "Order" is the aggregate type
private String orderId;
// ...
}
This tagging is crucial for maintaining the link between the event and its entity, enabling correct event sourcing and consistency boundary protection in AF5. For more details on how tagging works, see the Event tagging section.