Rollback a Saga

Hi everyone,
i’m trying to implementing a microservices application based on CQRS, event sourcing using Axon v.4. In the application there is a long transaction so i’m trying to create a Saga with Axon but i don’t understand how axon manages with the rollback. For example this is my saga:
create a new order (insert a new instance in pending mode)-> create a new ticket (insert a new instance)-> check that the consumerId exists -> order approved.
What can i do if for example the consumerId doesn’t exist? i wish that the application rollback but i don’t understand how.

https://github.com/ElenaBernardi/eFood_Axon (this is the link of the application but i didn’t commit the saga yet )

The way we’ve done it is by creating a rollback command and event. So, let’s take your situation where the consumer ID doesn’t exist. I’m using Axon 3.3.5 so the syntax I’m about to use may be slightly different than yours, but I think it will suffice. You might have a handler in your Saga class like so:

public void handle(NewTicketCreated event) {
commandGateway.send(checkConsumerIdCommand, new CommandCallback<CheckConsumerIdCommand, Object() {

@Override
public void onFailure(CommandMessage<?> extends CheckConsumerIdCommand> commandMessage, Throwable cause) {
commandGateway.send(new RollbackTicketCommand(ticketId));

}
};

}

So if sending the command to check the consumer ID fails the onFailure() callback method is invoked and a RollbackTicketCommand is sent through the gateway. You’d have a command handler in your Ticket aggregate which would change the aggregate’s state to some “rolled back” state and issue an event along those lines.

Basically, things aren’t going to work the way they do in an ACID transaction. Once an event is issued it is committed and immutable. So, the only way you can “undo” it is by issuing an event that does so.

Hope that helps!

You can send a command to undo something…e.g. CancelOrderCommand then you can change the state on your aggregate and trigger new events

Cheera
Matthias

thank you very much, it was so helpful, but i notice that in Axon v.4 the CommandCallback changed and i don’t know how to use it, do you have any idea?

@SagaEventHandler(associationProperty = "orderId")
public void on(NewTicketEvent evt){
    CheckConsumerIdCommand cmd = new CheckConsumerIdCommand(evt.getConsumerId());
    commandGateway.send(cmd, new CommandCallback<CheckConsumerIdCommand, Object>() {
        @Override
        public void onResult(CommandMessage<? extends CheckConsumerIdCommand> commandMessage, CommandResultMessage<?> commandResultMessage) {
            //what happen here?
        }
    });
}

Hi Elena, Michael and Matthias,

Elena, you could indeed use the CommandCallback directly, but I think there is a simpler way to dealing with the result of dispatching commands.
When using the CommandGateway#send method, the returned object is a CompletableFuture.
The CompletableFuture provides you with some nifty methods to perform an operation, like thenApply/thenAccept for successful operations or exceptionally when it has failed.

Important to note though, that when you are using the CompletableFuture (or the CommandCallback for that matter), that you are making that part of the code asynchronous.
As such, that will be a different thread then which initially called your SagaEventHandler annotated method.
Due to this, it is not recommended to perform state changes on the Saga in those calls.
You should only perform compensating actions, like Michael and Matthias suggest.

A ‘compensating actions’ could be for example dispatching another command to undo the initial operation.
It’s this task that you would perform on the “//what happen here?” comment.

Hope this sheds some light on the situation Elena!

Cheers,
Steven