1
votes

For a use case where I want to implement a Saga Orchestration for a couple services with Axon, I've built:

  • order-service (saga orchestrator with Spring boot + Axon Framework)
  • payment-service (microservice)
  • shipping-service (microservice)

Then I have a class annotated with @Saga with 3 steps/event handlers:

  • Order created (first service)
  • Payment done (second service)
  • Order shipped (third service)
  • Order effectively created

My question is how I'm supposed to rollback/compensate all previous executed steps if, for example, shipping service fails?

From documentation I've got some doubts on:

  • do I need to create a new class with @Saga?
  • on the service that failed should I throw an Exception or send error to another Command/Event
  • how should I call SagaLifecycle.end()

That would be great to have a solution showing some code. Thanks!

1
That would be great to have a solution showing some code. Thanks! - it would be great to have a question showing some code... :)tom redfern

1 Answers

2
votes

A well implemented Saga can deal with any faulty scenarios occurring from the transaction said Saga is managing. Thus, upon any operation dispatched from within the saga, you should be able to expect an exceptional case which you would react on. It is this reaction which is the compensating action you are looking for.

Now, to go over you exact questions:

do I need to create a new class with @Saga?

Pretty confident that you do not need a new saga to resolve the exceptional cases in your domain. So no.

on the service that failed should I throw an Exception or send error to another Command/Event

I would go either with an exception or an event here, not a command (as an (Axon) Saga does not handle commands). Whether you go either route depends on whether the event is a valuable business occurrence, thus requires to be stored indefinite in an event. Using an event will however mean you are splitting the fault tolerant behaviour of your Saga between distinct saga event handlers, which I personally wouldn't prefer. Thus, throwing an exception from one of the services to be handled by the saga would have my preference. Subsequently, if you need to store the exceptional occurrence, just publish an event next to it.

how should I call SagaLifecycle.end()

Not sure what you are looking for with this question to be honest. The SagaLifecycle.end() should be called once the Saga's lifecycle has ended. Thus, when that transaction is done. When it is done is entirely up to your domain and note something necessitated by the fact you need to dispatch a compensating action.

To clarify my intent, here's some (pseudo)code showing where I would perform the compensating action:

private transient CommandGateway commandGateway;

@SagaEventHandler(assocationProperty = "some-association)
public void on(SomeEvent event) {
    // Validate/set state if necessary
    commandGateway.send(new SomeCommand(...))
                  .exceptionally(exception -> {
                      // Dispatch compensating action through service/CommandGateway
                  });
}