A saga is a pattern used in a microservices architecture to manage multiple service calls that are a component of a bigger transaction. A saga is a sequence of local transactions that together implement a distributed transaction. The idea is that each service performs its local transaction, and if all the local transactions succeed, the saga is complete. The saga must be rolled back if one or more of the local transactions fail,
which requires undoing every finished local transaction.

To orchestrate a saga, a coordinator service is used. The coordinator service is responsible for initiating the saga and ensuring that each local transaction is completed successfully. The coordinator communicates with each service to initiate each local transaction and to ensure that each transaction is either committed or rolled back.

In a microservices design, there are two methods for orchestrating a saga: choreography-based and orchestration-based.
In choreography-based saga orchestration, each service is responsible for coordinating with other services, and the sequence of events is determined by the services themselves.

In orchestration-based saga orchestration, a centralized orchestrator service controls the sequence of events, and each service is notified by the orchestrator when it is time to execute its portion of the saga.

In a distributed system, complex transactions can be managed by saga orchestration, ensuring that even if some local transactions fail, the system is left in a consistent state and that all local transactions are completed properly.

SuuCat architecture
SuuCat architecture

In SuuCat (my sample app for microservices design patterns), Saga Orchestration has been implemented using RabbitMq and MassTransit.
OrderStateMachine is the state machine(centralized orchestrator) that controls the sequence of events in SuuCat.

In the below code, you can see the Commands, Events, and States of the OrderStateMachine.

Commands, Events, and States of the OrderStateMachine.
https://github.com/ebubekirdinc/SuuCat/blob/master/src/StateMachines/SagaOrchestrationStateMachine/StateMachines/OrderStateMachine.cs

Below, you can see, OrderStateMachine receives a CreateOrderMessage, sets the initial state of the saga, publishes an OrderCreatedEvent to notify other services, and transitions to the OrderCreated state. MassTransit provides the underlying infrastructure for managing the saga orchestration and coordinating the interactions between services.

https://github.com/ebubekirdinc/SuuCat/blob/master/src/StateMachines/SagaOrchestrationStateMachine/StateMachines/OrderStateMachine.cs

The Initially method is used to define the initial state of the state machine.
In this case, the CreateOrderMessage is the initial trigger for the state machine, which is sent from the Order Microservice.

https://github.com/ebubekirdinc/SuuCat/blob/master/src/Services/Order/src/Application/Order/Commands/CreateOrder/CreateOrderCommandHandler.cs

The When method is used to specify the event that triggers the transition from the current state to the next state. In this case, the CreateOrderMessage triggers the transition.

The second Then method is used to set the initial state of the saga. It sets various properties of the Saga object, such as the CustomerId, OrderId, CreatedDate, PaymentAccountId, and TotalPrice.

Then Publish method is used to publish an OrderCreatedEvent, which is used to notify other services that the order has been created. The CorrelationId property of the event is set to the same value as the CorrelationId property of the saga, which is used to correlate related messages in the saga. OrderCreatedEvent is received by the OrderCreatedEventConsumer in the Stock/Subscription Microservice and
the necessary actions regarding the order are made. Below you can see a section of the OrderCreatedEventConsumer from the beginning.

In order not to prolong it any further, we will try to focus on the OrderStateMachine without going into the details of consumer classes in this article.

https://github.com/ebubekirdinc/SuuCat/blob/master/src/Services/Subscription/src/Infrastructure/Consumers/Events/OrderCreatedEventConsumer.cs

After that TransitionTo is used to transition the state machine to the OrderCreated state after the OrderCreatedEvent has been published.

Now let’s go to the second part of the OrderStateMachine.

https://github.com/ebubekirdinc/SuuCat/blob/master/src/StateMachines/SagaOrchestrationStateMachine/StateMachines/OrderStateMachine.cs

In this part, we can see the next two states of the state machine after the OrderCreated state, based on whether the stock reservation for the order succeeds or fails.
If the stock reservation succeeds, a CompletePaymentMessage is sent to the payment service to complete the payment for the order, and the state machine transitions to the StockReserved state.
If the stock reservation fails, an OrderFailedEvent is published to notify other services that the order has failed, and the state machine transitions to the StockReservationFailed state.

Now let’s go to the last part in OrderStateMachine.

https://github.com/ebubekirdinc/SuuCat/blob/master/src/StateMachines/SagaOrchestrationStateMachine/StateMachines/OrderStateMachine.cs

This state machine’s last section explains how the saga will behave after the stock has been effectively reserved and the payment has either been successful or unsuccessful.
Here’s how it works:

During the StockReserved state:

  • When the PaymentCompletedEvent is received, the saga transitions to the PaymentCompleted state.
  • Then, the saga publishes the OrderCompletedEvent to indicate that the order has been successfully completed.
  • Finally, the state machine finalizes and completes the saga.

OR

  • When the PaymentFailedEvent is received, the saga publishes the OrderFailedEvent to indicate that the order has failed.
  • Then, the saga sends a StockRollbackMessage to roll back the stock reservation.
  • Finally, the saga transitions to the PaymentFailed state.

In conclusion, this section of the state machine deals with whether the payment is successful or unsuccessful after a successful stock allocation.
If the payment is successful, the purchase will be fulfilled, and the saga will be over.
If the payment is declined, the order is declined, the saga enters the PaymentFailed state, and a rollback message is sent to cancel the stock reservation.

We can use diagrams to make the subject more understandable. Here you can see the state diagram for the happy path of the order state machine, if everything goes right.
This happy path describes the successful flow of the order state machine, where the order is created, stock is reserved, payment is completed, and the order is completed successfully.

OrderStateMachine Happy Path Diagram

Below you can see a diagram of when stock reservation fails during the order process. Overall, this state machine ensures that the stock is properly reserved, and if it fails, the order process is rolled back and the appropriate events are published.

OrderStateMachine StockReservation Failed Diagram

And finally, here you can see a diagram of when the payment fails during the order process. The PaymentFailed state is reached when the payment processor returns a failed response to the system. When this happens, the system publishes an OrderFailedEvent, and responds to ensure that the state of the order and stock reservations are properly handled.

OrderStateMachine Payment Failed Diagram

References:

https://github.com/jasontaylordev/CleanArchitecture

https://www.gokhan-gokalp.com/en/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak/

--

--