jeromejhipolito / laravel-saga
Laravel package for Saga transaction pattern - manage distributed transactions with automatic rollback support
Installs: 11
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/jeromejhipolito/laravel-saga
Requires
- php: ^8.2
- illuminate/database: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
README
Laravel package for implementing the Saga transaction pattern - manage distributed transactions with automatic rollback support.
Features
- Step-by-step execution - Execute multiple steps in sequence with automatic tracking
- Automatic rollback - When a step fails, previously executed steps are automatically rolled back in reverse order
- Database persistence - Transaction and step states are persisted for debugging and recovery
- Status tracking - Detailed status enums for both transactions and steps
- Easy integration - Simple trait for Jobs and Consumers
Installation
composer require jeromejhipolito/laravel-saga
Publish and run the migrations:
php artisan vendor:publish --tag="saga-migrations"
php artisan migrate
Usage
Creating a Saga Step
Create a step by extending AbstractSagaStep:
<?php namespace App\Saga\Steps; use JeromeJHipolito\Saga\Steps\AbstractSagaStep; class CreateOrderStep extends AbstractSagaStep { public function execute(array $data): mixed { // Create the order $order = Order::create([ 'user_id' => $data['user_id'], 'total' => $data['total'], ]); return ['order_id' => $order->id]; } public function rollback(mixed $result, array $data): void { // Rollback: Delete the created order if (isset($result['order_id'])) { Order::find($result['order_id'])?->delete(); } } }
Using the UsesSaga Trait in Jobs
<?php namespace App\Jobs; use App\Saga\Steps\CreateOrderStep; use App\Saga\Steps\ReserveInventoryStep; use App\Saga\Steps\ProcessPaymentStep; use JeromeJHipolito\Saga\Traits\UsesSaga; class ProcessOrderJob implements ShouldQueue { use UsesSaga; public function handle(): void { $data = [ 'user_id' => $this->userId, 'items' => $this->items, 'total' => $this->total, ]; // Initialize the saga $saga = $this->initializeSaga($data); // Add steps $saga->addStep(new CreateOrderStep()) ->addStep(new ReserveInventoryStep()) ->addStep(new ProcessPaymentStep()); // Execute - if any step fails, previous steps are rolled back $this->executeSaga($data); } }
Direct Usage with SagaTransactionManager
<?php use JeromeJHipolito\Saga\Services\SagaTransactionManager; $saga = SagaTransactionManager::createTransaction( transactionId: 'order-'.Str::uuid(), jobClass: 'ProcessOrderJob', payload: $data ); $saga->addStep(new CreateOrderStep()) ->addStep(new ReserveInventoryStep()) ->addStep(new ProcessPaymentStep()); try { $result = $saga->execute($data); // Transaction completed successfully } catch (Exception $e) { // Transaction failed, steps were rolled back $saga->rollback(); // Manual rollback for additional cleanup }
Checking Transaction Status
// Find an existing transaction $saga = SagaTransactionManager::findByTransactionId('order-123'); $saga->isCompleted(); // true if all steps succeeded $saga->hasFailed(); // true if any step failed
Transaction and Step Statuses
Transaction Statuses:
PENDING- Transaction created, not yet startedRUNNING- Transaction is executing stepsCOMPLETED- All steps executed successfullyFAILED- A step failed during executionROLLING_BACK- Rollback is in progressROLLED_BACK- Rollback completed
Step Statuses:
PENDING- Step not yet executedEXECUTED- Step completed successfullyFAILED- Step execution failedROLLED_BACK- Step was rolled backCANCELLED- Step was never executed (skipped due to earlier failure)
Database Schema
The package creates two tables:
saga_transactions
id- Primary keyuuid- Unique identifiertransaction_id- Your custom transaction IDjob_class- The class that initiated the sagapayload- JSON payload datastatus- Current transaction statusstarted_at,completed_at,failed_at- Timestampsrollback_started_at,rollback_completed_at- Rollback timestampserror_message- Error details if failed
saga_steps
id- Primary keyuuid- Unique identifiersaga_transaction_id- Foreign key to transactionstep_name- Name of the stepstep_class- Full class name of the stepstep_order- Execution orderstatus- Current step statusexecuted_at,rollback_at- Timestampsresult_data- JSON result from executionerror_message- Error details if failed
Testing
composer test
License
The MIT License (MIT). Please see License File for more information.