Supported Parameters for Annotated Handlers
This chapter provides an exhaustive list of all the possible parameters for annotated message handling functions.
The framework resolves the parameters for any message handling function through an internal mechanism, called
the ParameterResolver.
The ParameterResolver, built by a ParameterResolverFactory, is in charge of inserting the parameters for annotated
command, event, and query handlers.
The set of ParameterResolvers can be extended if custom (or not yet) supported parameters should be injected in to
your annotated handlers.
You can configure additional ParameterResolvers by implementing the ParameterResolverFactory interface and
configuring the new implementation.
For more specifics on configuring custom `ParameterResolver`s we suggest
reading this section.
Supported parameters for command handlers
By default, @CommandHandler annotated methods allow the following parameter types.
| Parameter designation | Purpose |
|---|---|
The first parameter |
The first parameter is always the payload of the command message. It may also be of type |
type: |
Will contain the entire metadata of a |
annotated with |
Will resolve the metadata value with the key as indicated on the annotation. If |
type: |
Will get the complete message, with both the payload and the metadata. Resolving the entire |
type: |
Will get the current |
type: |
Will resolve the identifier of the handled |
type: |
Will inject the |
type: |
Will inject the |
Spring Bean |
If the application runs in a Spring environment, any Spring Bean can be resolved. The parameter can be annotated with |
Supported parameters for event handlers
By default, @EventHandler annotated methods allow the following parameter types.
| Parameter designation | Purpose |
|---|---|
The first parameter |
The first parameter is always the payload of the event message. It may also be of type |
type: |
Will contain the entire metadata of an |
annotated with |
Will resolve the metadata value with the key as indicated on the annotation. If |
type: |
Will get the complete message, with both the payload and the metadata. Resolving the entire |
type: |
Will get the current |
type: |
Will resolve the identifier of the handled |
type: |
Will resolve to the timestamp of the |
type: |
Will have the current token related to the processed event injected. Note that this will only work for streaming event processors, as otherwise there is no token attached to the events. |
type: |
Will resolve to the |
annotated with |
Will resolve to replay context values that were registered when the replay was started. This is only available during event replay and can be used to access replay-specific configuration or state. |
type: |
Will inject the |
type: |
Will inject the |
type: |
Will inject the |
Spring Bean |
If the application runs in a Spring environment, any Spring Bean can be resolved. The parameter can be annotated with |
|
Aggregate-specific parameters
If an aggregate-based event storage solution is used, there are a couple of additional parameter resolvers you can use:
None of the above are applicable when the event store is based of the dynamic consistency boundary principle, since events are not aggregate-specific in that scenario. |
Supported parameters for query handlers
By default, @QueryHandler annotated methods allow the following parameter types.
| Parameter designation | Purpose |
|---|---|
The first parameter |
The first parameter is always the payload of the query message. It may also be of type |
type: |
Will contain the entire metadata of a |
annotated with |
Will resolve the metadata value with the key as indicated on the annotation. If |
type: |
Will get the complete message, with both the payload and the metadata. Resolving the entire |
type: |
Will get the current |
type: |
Will resolve the identifier of the handled |
type: |
Will inject the |
type: |
Will inject the |
Spring Bean |
If the application runs in a Spring environment, any Spring Bean can be resolved. The parameter can be annotated with |
Examples
Here are some examples demonstrating the use of different parameter types:
Basic command handler with ProcessingContext
@CommandHandler
public void handle(PlaceOrderCommand command,
EventAppender appender) {
// Make business decision and append event...
PaymentRequestedEvent event = new PaymentRequestedEvent(
command.orderId(),
command.amount()
);
appender.append(event);
}
Event handler with ProcessingContext
@EventHandler
public void on(OrderPlacedEvent event,
ProcessingContext context) {
// Register cleanup action
context.whenComplete(pc -> cleanupResources());
processEvent(event);
}
Event handler with metadata and timestamp
@EventHandler
public void on(OrderPlacedEvent event,
@MetadataValue("userId") String userId,
@Timestamp Instant timestamp) {
// userId is extracted from metadata
// timestamp contains when the event was created
updateProjection(event, userId, timestamp);
}
Query handler with subscription query support
@QueryHandler
public OrderSummary handle(OrderQuery query,
QueryUpdateEmitter updateEmitter) {
OrderSummary summary = loadSummary(query.orderId());
OrderUpdate update = createUpdate(summary);
// Register for updates to send incremental changes
updateEmitter.emit(OrderQuery.class,
q -> q.orderId().equals(query.orderId()),
update);
return summary;
}
Event handler dispatching follow-up commands
@EventHandler
public CompletableFuture<Void> handle(OrderPlacedEvent event,
CommandDispatcher dispatcher) {
// Update the state based on the event
updateState(event);
// Dispatch follow-up command
CommandResult result = dispatcher.send(new ShipOrderCommand(event.orderId()));
// Return the CompletableFuture so the handler only completes when the command finishes
return result.getResultMessage()
.thenAccept(r -> logger.info("Shipping initiated"))
.exceptionally(ex -> {
logger.error("Failed to initiate shipping: {}", ex.getMessage());
// Handle error appropriately
return null;
});
}
Event handler accessing replay context
@EventHandler
public void on(OrderPlacedEvent event,
ReplayStatus replayStatus,
@ReplayContext String replayReason) {
if (replayStatus == ReplayStatus.REPLAY) {
// Special handling during replay
logger.info("Replaying event due to: {}", replayReason);
// Skip side effects during replay
} else {
// Normal processing
sendEmailNotification(event);
}
}