Saga Pattern
debt(d9/e9/b9/t7)
Closest to 'silent in production until users hit it' (d9). detection_hints.automated is no; saga bugs (missing compensations, lost correlation IDs) manifest as data inconsistency in production, no tool catches them.
Closest to 'architectural rework' (e9). Adding compensating transactions, correlation IDs, and orchestrator state persistence per quick_fix is not a patch — it's redesigning the distributed transaction flow across multiple services.
Closest to 'defines the system's shape' (b9). Per applies_to (web/cli/queue) and tags (distributed-transactions, microservices), saga is the backbone of cross-service consistency; every new cross-service operation must conform to it.
Closest to 'serious trap' (t7). Per misconception, devs assume ACID semantics but get ACI with no isolation — directly contradicts how local DB transactions behave, leading to wrong assumptions about rollback and visibility.
TL;DR
Explanation
Distributed transactions across microservices can't use 2PC (too slow, too coupled). Saga: a sequence of local transactions (T1, T2, ..., Tn) where each Ti publishes an event triggering Ti+1. On failure at Ti: run compensating transactions (C(i-1), ..., C1) to undo. Two styles: choreography (each service publishes events and other services react — no central coordinator) and orchestration (a Saga orchestrator sends commands to services and handles failures centrally). Choreography: decoupled but hard to trace. Orchestration: easier to monitor and debug. PHP: Symfony Messenger workflows, custom saga state machine.
Common Misconception
Why It Matters
Common Mistakes
- No compensating transactions defined — partial failures leave data inconsistent.
- Choreography without correlation ID — impossible to trace saga execution.
- Not handling compensating transaction failures — need retry + alerting.
Code Examples
// 2PC across services — tight coupling, deadlock risk:
$coordinator->prepare('order-service', $orderTx);
$coordinator->prepare('payment-service', $paymentTx);
$coordinator->commit(); // Both or neither
// Saga orchestration:
// 1. OrderService.createOrder() → publishes OrderCreated
// 2. PaymentService.processPayment() → publishes PaymentProcessed
// 3. InventoryService.reserveItems() → publishes ItemsReserved
// On PaymentFailed: publish OrderCancelled → inventory released
$sagaState = ['order_id' => $orderId, 'step' => 'payment', 'status' => 'pending'];
// Orchestrator retries failed steps, triggers compensations on unrecoverable failure