The Saga pattern is a design pattern used to manage and coordinate distributed transactions in microservices architectures. Unlike traditional monolithic transactions that ensure ACID (Atomicity, Consistency, Isolation, Durability) properties through a single database transaction, distributed transactions span multiple services, making it difficult to maintain ACID properties. The Saga pattern addresses this by breaking down a transaction into a series of smaller, independent steps (or sub-transactions) that are executed in a predefined sequence.
Each step in a saga has a corresponding compensating transaction to undo the changes made by the step if something goes wrong. This ensures eventual consistency rather than immediate consistency.
Example in .NET
Let's implement a simple example in .NET where a saga is used to manage an order processing workflow involving multiple microservices: InventoryService, PaymentService, and ShippingService.
- InventoryService: Reserves inventory for the order.
- PaymentService: Processes the payment.
- ShippingService: Ships the order.
Each service will have a compensating transaction to undo its action if something goes wrong.
Step 1. Define the Services
Keep the tutorial simple. Instead of creating separate services, I have just created simple classes. In real-world use cases, these services would be independently deployable components.
public class InventoryService
{
public async Task<bool> ReserveInventoryAsync(Order order)
{
Console.WriteLine($"Reserving inventory for order {order.Id}");
await Task.Delay(100); // Simulating async work
return true; // Assume success for simplicity
}
public async Task UndoReserveInventoryAsync(Order order)
{
Console.WriteLine($"Undoing inventory reservation for order {order.Id}");
await Task.Delay(100); // Simulating async work
}
}
public class IPaymentService
{
public async Task<bool> ProcessPaymentAsync(Order order)
{
Console.WriteLine($"Processing payment for order {order.Id}");
await Task.Delay(100); // Simulating async work
return true; // Assume success for simplicity
}
public async Task UndoProcessPaymentAsync(Order order)
{
Console.WriteLine($"Undoing payment for order {order.Id}");
await Task.Delay(100); // Simulating async work
}
}
public class IShippingService
{
public async Task<bool> ShipOrderAsync(Order order)
{
Console.WriteLine($"Shipping order {order.Id}");
await Task.Delay(100); // Simulating async work
return true; // Assume success for simplicity
}
public async Task UndoShipOrderAsync(Order order)
{
Console.WriteLine($"Undoing shipping for order {order.Id}");
await Task.Delay(100); // Simulating async work
}
}
Step 2. Define the Saga
The OrderSagaService class orchestrates the steps involved in processing an order, coordinating the interactions between the InventoryService, PaymentService, and ShippingService. If any step fails, it triggers a compensation mechanism to undo previously completed steps, ensuring the system maintains a consistent state
public class OrderSagaService
{
private readonly InventoryService _inventoryService;
private readonly IPaymentService _paymentService;
private readonly IShippingService _shippingService;
public OrderSagaService(InventoryService inventoryService,
IPaymentService paymentService,
IShippingService shippingService)
{
_inventoryService = inventoryService;
_paymentService = paymentService;
_shippingService = shippingService;
}
public async Task<bool> ProcessOrderAsync(Order order)
{
try
{
bool inventoryReserved = await _inventoryService.ReserveInventoryAsync(order);
if (!inventoryReserved) throw new Exception("Failed to reserve inventory");
bool paymentProcessed = await _paymentService.ProcessPaymentAsync(order);
if (!paymentProcessed) throw new Exception("Failed to process payment");
bool orderShipped = await _shippingService.ShipOrderAsync(order);
if (!orderShipped) throw new Exception("Failed to ship order");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
await CompensateAsync(order);
return false;
}
}
private async Task CompensateAsync(Order order)
{
await _shippingService.UndoShipOrderAsync(order);
await _paymentService.UndoProcessPaymentAsync(order);
await _inventoryService.UndoReserveInventoryAsync(order);
}
}

Join the conversation! Your thoughts help the community grow.