SYS://VISION.ACTIVE
VIEWPORT.01
LAT 28.0222° N
SIGNAL.NOMINAL
VISION Loading
Back to Blog

Saga Pattern: Managing Distributed Transactions

Shane Barron

Shane Barron

Laravel Developer & AI Integration Specialist

The Problem with Distributed Transactions

In distributed systems, traditional ACID transactions don't work across service boundaries. The saga pattern provides eventual consistency through a sequence of local transactions with compensating actions for failures.

Saga Types

Choreography

// Services react to events
class OrderCreated { }
// → Inventory reserves stock → StockReserved
// → Payment charges card → PaymentCompleted
// → Shipping creates shipment → ShipmentCreated

class StockReservedHandler
{
    public function handle(StockReserved $event): void
    {
        // Trigger next step
        event(new ProcessPayment($event->orderId));
    }
}

Orchestration

class OrderSaga
{
    private array $completedSteps = [];

    public function execute(Order $order): void
    {
        try {
            $this->reserveInventory($order);
            $this->completedSteps[] = 'inventory';

            $this->processPayment($order);
            $this->completedSteps[] = 'payment';

            $this->createShipment($order);
            $this->completedSteps[] = 'shipment';

            $this->completeOrder($order);
        } catch (Exception $e) {
            $this->compensate($order);
            throw $e;
        }
    }

    private function compensate(Order $order): void
    {
        foreach (array_reverse($this->completedSteps) as $step) {
            match ($step) {
                'shipment' => $this->cancelShipment($order),
                'payment' => $this->refundPayment($order),
                'inventory' => $this->releaseInventory($order),
            };
        }
    }
}

Saga State Management

class SagaState extends Model
{
    protected $casts = [
        'completed_steps' => 'array',
        'failed_at' => 'datetime',
    ];

    public function markStepComplete(string $step): void
    {
        $steps = $this->completed_steps ?? [];
        $steps[] = $step;
        $this->update(['completed_steps' => $steps]);
    }

    public function markFailed(string $reason): void
    {
        $this->update([
            'status' => 'failed',
            'failure_reason' => $reason,
            'failed_at' => now(),
        ]);
    }
}

Idempotency

class IdempotentHandler
{
    public function handle(ProcessPayment $command): void
    {
        $key = "payment_{$command->orderId}";

        if (Cache::has($key)) {
            return; // Already processed
        }

        $this->payment->charge($command->amount);
        Cache::put($key, true, now()->addDays(7));
    }
}

Conclusion

Sagas provide eventual consistency in distributed systems. Choose choreography for simple flows, orchestration for complex ones. Always implement compensating actions and ensure idempotency.

Share this article
Shane Barron

Shane Barron

Strategic Technology Architect with 40 years of experience building production systems. Specializing in Laravel, AI integration, and enterprise architecture.

Need Help With Your Project?

I respond to all inquiries within 24 hours. Let's discuss how I can help build your production-ready system.

Get In Touch