Saga Execution Event Listener

ExecutionEventListener<A> is where it is notified about each and every action during the transaction lifecycle. As an example, After handing over the transaction to the StackSaga engine by mentioning the starting executor, the engine will invoke your executors one by one. During the process, the engine does provide some events regarding the execution to notify. To get notified for each action, you can create a separate ExecutionEventListener implementation for Aggregators. One aggregator can have many ExecutionEventListener implementations as needed. Specially the ExecutionEventListener is provided for cross-cutting concerns.

ExecutionEventListener comes with a generic parameter. the <A> is represented the target Aggregator. when the ExecutionEventListener implementation is created, the target aggregator should be mentioned.

ExecutionEventListener<A> provides the following events to be notified.

  1. onEachPrimaryExecutionPerformed

    • This method is invoked after executing each primary execution successfully. if there is any error, it is not called. if there is a RetryableExecutorException the transaction is scheduled for retrying and then if the next time it is successful, this method is called. if there is a NonRetryableExecutorException the transaction is scheduled for Compensating and this method is not called.

  1. onPrimaryExecutionException

    • This method is invoked when there is a NonRetryableExecutorException during the primary execution. that means that due to the primary execution is failed with a NonRetryableExecutorException, this method is invoked. it is the point before starting the Compensating process.

  1. onEachRevertPerformed

    • This is the opposite of the onEachPrimaryExecutionPerformed.

    • This method is invoked after executing each revert execution successfully. if there is a RetryableExecutorException the transaction is scheduled for retrying and then if the next time it is successful, this method is called.

      As per the architecture, there can’t be any unhandled-exception(RuntimeException) during the revert process. because the compensating can only have RetryableExecutorException. if an unhandled-exception(RuntimeException) is thrown during the revert process, the framework will terminate the transaction. there is no more chance to revert the transaction.
  1. onTransactionCompleted

    • This method is invoked when the transaction is completed.

    • The transaction can be completed in 2 ways. the transaction can be completed successfully by processing all the primary executions successfully or the transaction can be completed after processing all the compensating executions successfully.

  1. onTransactionTerminated

    • as discussed above, the transaction can be terminated when there is an unhandled-exception(RuntimeException) during the revert process.

    • This method is invoked when the transaction is terminated.

In ExecutionEventListener interface, all the abstract methods are default. Therefore, it is not required to override all the methods, you can override the methods that you need. Here you can see an example how you can create a listener of ExecutionEventListener.
public class PlaceOrderEventListener implements ExecutionEventListener<PlaceOrderAggregator> {
    @Override
    public void onEachProcessExecutionPerformed(String executedExecutor, PlaceOrderAggregator currentAggregator) {
        //TODO: implement your logic here
    }

    @Override
    public void onProcessException(PlaceOrderAggregator finalAggregatorState, NonRetryableExecutorException exception, String executedExecutor) {
        //TODO: implement your logic here
    }


    @Override
    public void onEachRevertPerformed(String revertedExecutor, PlaceOrderAggregator finalAggregatorState,
                                      NonRetryableExecutorException nonRetryableExecutorException,
                                      RevertHintStore revertHintStore
    ) {
        //TODO: implement your logic here

    }

    @Override
    public void onTransactionCompleted(TransactionCompletedDetail<PlaceOrderAggregator> transactionCompletedDetail) {
        //TODO: implement your logic here
    }

    @Override
    public void onTransactionTerminated(TransactionTerminationDetail<PlaceOrderAggregator> transactionTerminationDetail) {
        //TODO: implement your logic here
    }
}
It is important not to make any changes to the aggregator and other parameters that are passed to the listener methods. it can be very harmful to the transaction execution. just use the parameters to read.