Featured image of post Understanding the SAGA architecture pattern

Understanding the SAGA architecture pattern

To manage distributed transactions in microservices

What the Heck is SAGA? ๐Ÿค”

SAGA is a design pattern used to manage distributed transactions in microservices. When youโ€™ve got multiple services talking to each other, ensuring data consistency can be a nightmare. SAGA swoops in like a superhero to coordinate things smoothly.

Also: “saga” does not actually stand for anything as it’s not an acronym; instead, the term is borrowed from storytelling, signifying a sequence of events that form a complete narrative,

So Whatโ€™s the Problem we need to solve?

In a monolithic system, transactions are simple. You wrap everything in a database transaction (BEGIN TRANSACTION โ€ฆ COMMIT), and if anything fails, you roll it back (ROLLBACK).

But in microservices, each service has its own database. So, if Service A calls Service B, which calls Service C, and something breaks halfway through, you canโ€™t just roll back everything.

Enter SAGA. Instead of a single transaction, it breaks things into a series of smaller transactions, ensuring consistency through compensating actions (i.e., undo operations).

How SAGA Works ๐Ÿ› ๏ธ

SAGA ensures that if something fails mid-way, the system doesnโ€™t end up in a half-baked state. There are two main ways to implement it:

1. Choreography ๐Ÿ•บ

Each service is like a well-trained dancerโ€”it knows its moves and what to do next. Thereโ€™s no central brain; each service reacts to events and triggers the next step.

  • Example: You book a flight, which triggers a hotel booking, which triggers a car rental.
  • If the hotel booking fails, an event is emitted to cancel the flight.

Pros:

โœ… Simple to implement for small workflows
โœ… No central coordinator required

Cons:

โŒ Can become messy with too many services
โŒ Harder to debug and track events

2. Orchestration ๐ŸŽป

Here, a central orchestrator (think of a conductor in an orchestra) manages the entire flow. It calls each service in sequence and handles rollbacks if anything goes wrong.

  • Example: A central Order Service coordinates payments, shipping, and inventory.
  • If shipping fails, the orchestrator triggers a refund.

Pros:

โœ… More control and visibility
โœ… Easier debugging

Cons:

โŒ Adds complexity (you need an orchestrator service)
โŒ Can become a single point of failure (unless made resilient)

Real-World Example: Ordering a Pizza ๐Ÿ•

Letโ€™s say you order a pizza. Hereโ€™s how SAGA keeps things sane:

  1. Order Service โ†’ Receives order and initiates payment.
  2. Payment Service โ†’ Charges your card.
  3. Kitchen Service โ†’ Starts making the pizza.
  4. Delivery Service โ†’ Assigns a delivery guy.
  5. Success! ๐ŸŽ‰ Your pizza arrives, and life is good.

But what if something goes wrong?

  • If Payment fails โ†’ Cancel the order.
  • If the Kitchen burns your pizza โ†’ Refund the payment.
  • If no delivery driver is available โ†’ Offer pickup instead.

Choreography Example: Order Processing (C#) ๐Ÿš€

Hereโ€™s a choreographed SAGA for an order processing system in C#:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Threading.Tasks;

public class OrderService
{
    public async Task PlaceOrder()
    {
        Console.WriteLine("Order placed.");
        await PaymentService.ProcessPayment();
    }
}

public static class PaymentService
{
    public static async Task ProcessPayment()
    {
        Console.WriteLine("Payment processed.");
        await ShippingService.ShipOrder();
    }
}

public static class ShippingService
{
    public static async Task ShipOrder()
    {
        Console.WriteLine("Order shipped.");
    }
}

// Simulating the flow
await new OrderService().PlaceOrder();

Breakdown:

โœ… Each service does its part and calls the next step.
โœ… No central coordinator.
โŒ If something fails, rolling back is tricky! ๐Ÿ˜ฌ


Orchestration Example: Order Processing (Python) ๐Ÿ

Now, letโ€™s do the same thing using a central orchestrator in Python:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from fastapi import FastAPI

app = FastAPI()

class OrderSaga:
    def __init__(self):
        self.state = "START"

    async def place_order(self):
        print("Order placed.")
        self.state = "PAYMENT"
        return await self.process_payment()

    async def process_payment(self):
        print("Payment processed.")
        self.state = "SHIPPING"
        return await self.ship_order()

    async def ship_order(self):
        print("Order shipped.")
        self.state = "COMPLETED"
        return {"status": "success"}

order_saga = OrderSaga()

@app.post("/order")
async def order():
    return await order_saga.place_order()

Breakdown:

โœ… Orchestrator (OrderSaga) manages the flow.
โœ… Easier rollback handling.
โŒ Slightly more complex to set up.


Which One Should You Use? ๐Ÿคท

PatternProsCons
ChoreographySimple, no central service neededHard to debug, complex failure handling
OrchestrationMore control, easier rollbackAdds complexity, requires extra service