Component Message Intercepting
|
Declarative and annotated interceptor support for Message Handling Components will be introduced in 5.2.0. As such, the description below is not complete nor finalized. For those curious, be sure to follow the issue that will implement this functionality. |
@CommandHandlerInterceptor annotation
You can add a handler interceptor as a @CommandHandlerInterceptor annotated method on the aggregate or entity.
This allows you to make decisions based on the current state of the aggregate:
public class GiftCard {
private String state;
@CommandHandlerInterceptor
public void intercept(RedeemCardCommand command, InterceptorChain chain) {
if (this.state.equals(command.getState())) {
chain.proceed();
}
// Otherwise, don't proceed - command is blocked
}
@CommandHandler
public void handle(RedeemCardCommand command) {
// Handle the command
}
}
Note that the @CommandHandlerInterceptor is essentially a more specific implementation of the @MessageHandlerInterceptor described below.
Annotated @MessageHandlerInterceptor
Alongside defining overall MessageHandlerInterceptor instances on the component handling a message, you can also define a handler interceptor for a specific component containing the handlers.
This is achieved by adding a method with the @MessageHandlerInterceptor annotation.
Several options are available:
-
InterceptorChain parameter: The
InterceptorChainis an optional parameter which can be added to the intercepting method. In absence of this parameter, the framework will callInterceptorChain#proceedonce the method exits. -
Message type filtering: You can define the type of
Messagethe interceptor should deal with using themessageTypeparameter. -
Payload type filtering: For fine-grained control, specify the
payloadTypecontained in theMessage.
Simple @MessageHandlerInterceptor method:
public class CardSummaryProjection {
@MessageHandlerInterceptor
public void intercept(Message message) {
// Intercepts all messages to this component
}
@EventHandler
public void on(CardIssuedEvent event) {
// Handle event
}
}
Defining the message type:
public class CardSummaryProjection {
@MessageHandlerInterceptor(messageType = EventMessage.class)
public void intercept(EventMessage event) {
// Intercepts only event messages
}
}
Defining message and payload type:
public class CardSummaryProjection {
@MessageHandlerInterceptor(
messageType = EventMessage.class,
payloadType = CardRedeemedEvent.class
)
public void intercept(CardRedeemedEvent event) {
// Intercepts only CardRedeemedEvent messages
}
}
Using InterceptorChain for control:
public class CardSummaryProjection {
@MessageHandlerInterceptor(messageType = QueryMessage.class)
public void intercept(QueryMessage query, InterceptorChain chain) throws Exception {
// Logic before handling
chain.proceed();
// Logic after handling
}
}
Hierarchy on aggregate member and root:
public class GiftCard {
@AggregateIdentifier
private String id;
@AggregateMember
private List<GiftCardTransaction> transactions = new ArrayList<>();
@MessageHandlerInterceptor
public void intercept(Message message) {
// This interceptor will be invoked FIRST
}
}
public class GiftCardTransaction {
@EntityId
private String transactionId;
@MessageHandlerInterceptor
public void intercept(Message message) {
// This interceptor will be invoked SECOND
}
}
@MessageHandlerInterceptor hierarchy for @EventSourcingHandler
However, this support does not exist for event sourcing handlers!
Only the interceptors on that level of the hierarchy are invoked.
In the example above, when the This is a known discrepancy that cannot be easily adjusted in Axon’s current structure.
It is not recommended to rely on an invocation hierarchy for |
Next to the message, payload, and InterceptorChain, a @MessageHandlerInterceptor annotated method can resolve other parameters as well.
Which parameters the framework can resolve depends on the type of Message being handled by the interceptor.
For more specifics, see Supported Parameters.
@ExceptionHandler
The @MessageHandlerInterceptor also allows for a more specific version: an @ExceptionHandler annotated method.
The framework invokes @ExceptionHandler annotated methods only for exceptional results of message handling.
This allows you to throw more domain-specific exceptions or translate technical exceptions to business errors.
You can wire all default parameters to an exception handler, similar to command, event, and query handlers. Hence, you can add the exception, payload, metadata, and other options.
You can introduce @ExceptionHandler annotated methods in any message handling component, including sagas.
You can choose to react to all exceptions or define specific exception/message combinations.
Aggregate exception handlers:
class GiftCard {
// State, command handlers and event sourcing handlers omitted
@ExceptionHandler
public void handleAll(Exception exception) {
// Handles all exceptions thrown within this component
}
@ExceptionHandler
public void handleIssueCardExceptions(IssueCardCommand command) {
// Handles all exceptions from the IssueCardCommand handler
}
@ExceptionHandler(payloadType = IssueCardCommand.class)
public void handleIssueCardExceptions() {
// Handles all exceptions from the IssueCardCommand handler
}
@ExceptionHandler
public void handleIllegalStateExceptions(IllegalStateException exception) {
// Handles all IllegalStateExceptions thrown within this component
}
@ExceptionHandler(resultType = IllegalStateException.class)
public void handleIllegalStateExceptions(Exception exception) {
// Handles all IllegalStateExceptions thrown within this component
}
@ExceptionHandler
public void handleSpecificCase(IssueCardCommand command,
IllegalStateException exception) {
// Handles IllegalStateExceptions from IssueCardCommand handler
}
@ExceptionHandler(
resultType = IllegalStateException.class,
payloadType = IssueCardCommand.class
)
public void handleSpecificCase() {
// Handles IllegalStateExceptions from IssueCardCommand handler
}
}
|
Exception Handling for Aggregate Constructors
The If you expect exceptions on an aggregate’s constructor that you need to handle differently, use Axon’s creation policy. |
Projector exception handlers:
class CardSummaryProjection {
// Event handlers and query handlers omitted
@ExceptionHandler
public void handleAll(Exception exception) {
// Handles all exceptions thrown within this component
}
@ExceptionHandler
public void handleFindCardQueryExceptions(FindCardQuery query) {
// Handles all exceptions from the FindCardQuery handler
}
@ExceptionHandler(payloadType = FindCardQuery.class)
public void handleFindCardQueryExceptions() {
// Handles all exceptions from the FindCardQuery handler
}
@ExceptionHandler
public void handleIllegalArgumentExceptions(IllegalArgumentException exception) {
// Handles all IllegalArgumentExceptions within this component
}
@ExceptionHandler(resultType = IllegalArgumentException.class)
public void handleIllegalArgumentExceptions(Exception exception) {
// Handles all IllegalArgumentExceptions within this component
}
@ExceptionHandler
public void handleSpecificCase(CardIssuedEvent event,
IllegalArgumentException exception) {
// Handles IllegalArgumentExceptions from CardIssuedEvent handler
}
@ExceptionHandler(
resultType = IllegalArgumentException.class,
payloadType = CardIssuedEvent.class
)
public void handleSpecificCase() {
// Handles IllegalArgumentExceptions from CardIssuedEvent handler
}
}