4

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é

polosoft
  • 200
  • 1
  • 14

2 Answers2

4

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(...));
                                     });
    }
}
Steven
  • 6,936
  • 1
  • 16
  • 31
  • Can you please let us know how can compensating transaction be achived using this framework? – PAA Mar 29 '21 at 04:32
  • The key is in the word "action" here. A "command == action", so a compensating action is nothing more than sending a type of command. The fact I don't use command there, is because what an action is depends very much on your own domain. – Steven Mar 29 '21 at 08:12
  • 1
    So to put it more concretely, you would catch the exception and in the catch block or `CompletableFuture#exceptionally` method you'd then perform the compensating action. This compensating action could, for example, be dispatching a command to rollback the change. Ergo, you would use the `CommandGateway` or `CommandBus` to that end, as those are used to dispatch commands. – Steven Mar 29 '21 at 08:14
  • As you might have noticed, there is no real framework-magic to help you here. This is simply the case as Axon Framework cannot know what the action is you need to perform to rollback. It simply provides the infrastructure for CQRS, DDD and Event Sourcing, with messaging at it's basis. The key you use for this problem, is its messaging infra, to be able to dispatch the compensating action. – Steven Mar 29 '21 at 08:15
  • Thanks for clarification. Its very clear now. I was expecting Axon will do it free for us. Could you share some sample code on how the compensating transactions can be done using Axon that will be really helpful. – PAA Mar 29 '21 at 08:36
  • Added some pseudo-code to the original answer. – Steven Mar 29 '21 at 09:30
1

I don't know your exact use case, but from this and your previous question I get the impression you want to roll back, or in this case undo, the event if one of the event handlers cannot process it.

In general, there are some things you are able to do. You can see if the aggregate that applied the event in the first place has or can have the information to check whether the 'slave' microservice should be able to handle the event before you apply it. If this isn't practical, the slave microservice can also apply a 'failure' event directly on the eventbus to inform the rest of the system that a failure state has occurred that needs to be handled:

https://docs.axoniq.io/reference-guide/implementing-domain-logic/event-handling/dispatching-events#dispatching-events-from-a-non-aggregate

Mzzl
  • 3,926
  • 28
  • 39