Search code examples
javaspring-bootsagaaxon

Axon Framework: Saga project with compensation events between two or three microservices


I have a question about Axon Saga. I have a project where I have three microservices, each microservice has his own database, but the two "Slave" microservice has to share his data to the "Master" microservice, for that I want to use the Axon Saga. I already asked a question about the compensation, when something goes wrong, and I have to deal with the compensation by myself, it is ok, but not ideal. Currently I am using the DistributedCommandBus to communicate between the microservices, is it good for that? I am using the Choreography Saga model, so here is what it is look like now:

  1. Master -> Send command -> Slave1 -> Handles event
  2. Slave1 -> Send back command -> Master -> Handles event
  3. Master -> Send command -> Slave2 -> Handles event
  4. Slave2 -> Send back command -> Master -> Handles event

If something went wrong then comes the compensating Commands/Events backwards.

My question is has anybody did something like this with Axon, with compensation, what the best practices for that? How can I retry the Saga process? With the RetryScheduler? Add a github repo if you can.

Thanks, Máté


Solution

  • First and foremost, let me answer your main question:

    My question is has anybody did something like this with Axon?

    Shortly, yes, as this is one of the main use cases of for Sagas. As a rule of thumb, I'd like to state a Saga can be used to coordinate a complex business transaction between:

    1. Several distinct Aggregate Instances
    2. Several Bounded Contexts

    On face value, it seems you've landed in option two of delegating a complex business transaction.

    It is important to note that when you are using Sagas, you should very consciously deal with any exceptions and/or command dispatching results.

    Thus, if you dispatch a command from the "Master" to "Slave 1" and the latter fails the operation, this result will come back in to the Saga. This thus gives you the first option to retry an operation, which I would suggest to do with a compensating action. Lastly, with a compensating action, I am talking about dispatching a command to trigger it.

    If you can not rely on the direct response from dispatching the command, retrying/rescheduling a message within the Saga would be a reasonable second option.

    To that end, Axon has the EventScheduler and DeadlineManager. Note that the former of the two publishes an event for everyone to see. The latter schedules a DeadlineMessage within the context of that single Saga instance, thus limiting the scope of who can see a retry is occurring.

    Typically, the DeadlineManager would be my preferred mode of operation for thus, unless you require this 'rescheduling action' to be seen by everybody. FYI, check this page for EventScheduler information and this page for DeadlineManager info.

    Sample Update

    Here's a bit of pseudo-code to get a feel what a compensating action in a Saga Event Handler would look like:

    class SomeSaga {
    
        private CommandGateway commandGateway;
    
        @SagaEventHandler(assocationValue = "some-key")
        public void on(SomeEvent event) {
            // perform some checks, validation and state setting, if necessary
            commandGateway.send(new MyActionCommand(...))
                          .exceptionally(throwable -> {
                                             commandGateway.send(new CompensatingAction(...));
                                         });
        }
    }