Design Patterns & Practices  

🌀 Saga Design Pattern (Choreography Style) Cheat Sheet

Style: No central orchestrator → services talk via events
Flow: Each service emits an event → others react & continue
Rollback: Compensating events

📦 Example – Order Workflow (Event-driven)

Scenario:

  1. Order Service → creates order → publishes OrderCreated

  2. Payment Service → listens to OrderCreated, reserves money → publishes PaymentCompleted (or PaymentFailed)

  3. Inventory Service → listens to PaymentCompleted, updates stock → publishes InventoryUpdated

  4. If any failure → publish compensation events (OrderCancelled, PaymentRefunded)

🖥️ C# Example

// Event definitions
public record OrderCreated(Guid OrderId);
public record PaymentCompleted(Guid OrderId);
public record PaymentFailed(Guid OrderId);
public record InventoryUpdated(Guid OrderId);

// Simple Event Bus (Pub/Sub)
public class EventBus
{
    private readonly Dictionary<Type, List<Func<object, Task>>> _handlers = new();

    public void Subscribe<T>(Func<T, Task> handler)
    {
        if (!_handlers.ContainsKey(typeof(T)))
            _handlers[typeof(T)] = new List<Func<object, Task>>();

        _handlers[typeof(T)].Add(async e => await handler((T)e));
    }

    public async Task Publish<T>(T @event)
    {
        if (_handlers.ContainsKey(typeof(T)))
            foreach (var handler in _handlers[typeof(T)])
                await handler(@event);
    }
}

Services

public class OrderService
{
    private readonly EventBus _bus;
    public OrderService(EventBus bus) => _bus = bus;

    public async Task CreateOrder(Guid orderId)
    {
        Console.WriteLine("✅ Order Created");
        await _bus.Publish(new OrderCreated(orderId));
    }
}

public class PaymentService
{
    private readonly EventBus _bus;
    public PaymentService(EventBus bus)
    {
        _bus = bus;
        _bus.Subscribe<OrderCreated>(HandleOrderCreated);
    }

    private async Task HandleOrderCreated(OrderCreated e)
    {
        Console.WriteLine("💳 Payment Reserved");
        await _bus.Publish(new PaymentCompleted(e.OrderId));
    }
}

public class InventoryService
{
    public InventoryService(EventBus bus)
    {
        bus.Subscribe<PaymentCompleted>(HandlePaymentCompleted);
    }

    private async Task HandlePaymentCompleted(PaymentCompleted e)
    {
        Console.WriteLine("📦 Inventory Updated for Order " + e.OrderId);
    }
}

Usage

var bus = new EventBus();
var orderService = new OrderService(bus);
var paymentService = new PaymentService(bus);
var inventoryService = new InventoryService(bus);

await orderService.CreateOrder(Guid.NewGuid());

🌍 Real-World Example

  • Food Delivery App

    • Order placed → Payment deducted → Restaurant notified → Delivery assigned

    • If Payment fails → OrderCancelled event triggers rollback

📌 Interview Quick Notes

  • Orchestration = Central coordinator.

  • Choreography = Event-driven, decentralized.

  • Use when services should be loosely coupled.

  • Harder to track/monitor (no central brain).