What is the Saga pattern, and how does it manage distributed transactions in microservices?

The Saga pattern coordinates a business transaction that spans multiple microservices by breaking it into a sequence of local transactions, one per service, instead of relying on a single distributed transaction.

Key Points: • Each local transaction updates its own service's database and then triggers the next step, usually via an event or a direct command. • Two coordination styles exist: choreography, where services react to each other's events with no central controller, and orchestration, where a saga orchestrator directs each step explicitly. • Every step that changes state needs a compensating transaction so its effect can be undone if a later step fails. • Because there is no distributed lock across services, the Saga pattern gives up strict ACID atomicity in favor of eventual consistency. • A saga/correlation ID is tracked through the whole flow so the system can resume or roll back an in-flight saga after a crash.

Example: An e-commerce checkout might involve Order, Payment, and Inventory services. Order creates a pending order, Payment charges the customer, and Inventory reserves stock; if Inventory reservation fails, compensating transactions refund the payment and cancel the order, so the system never ends up in a state where money was taken but no order exists.

Code Example:

public interface SagaStep<T> {
    void execute(T context);
    void compensate(T context);
}

public class OrderSagaOrchestrator {
    private final List<SagaStep<OrderContext>> steps;

    public void run(OrderContext ctx) {
        int completed = 0;
        try {
            for (SagaStep<OrderContext> step : steps) {
                step.execute(ctx);
                completed++;
            }
        } catch (Exception ex) {
            for (int i = completed - 1; i >= 0; i--) {
                steps.get(i).compensate(ctx);
            }
        }
    }
}

Interview Tip: A concise interview answer is:

"The Saga pattern splits a distributed transaction into local transactions per service, each with a compensating action, coordinated either through choreography with events or a central orchestrator. It trades strict atomicity for eventual consistency, which is necessary once each service owns its own database."