Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

Subscribe to the newsletter

Get new articles and course lessons delivered to your inbox. No spam, unsubscribe anytime.

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
Lesson 20 of the Advanced Laravel: Architecture, Scaling & Performance course
LaravelJune 28, 20263 min read

Distributed Transactions and Sagas: Managing Consistency in Laravel

Learn how to implement the Saga pattern to manage distributed transactions across microservices, ensuring data consistency with compensating actions.

LaravelArchitectureMicroservicesDistributed SystemsSaga Patternphpbackend

Previously in this course, we explored Event-Driven Architecture to decouple our services. While events provide great flexibility, they introduce the "dual write" problem: how do we ensure a database update and an event dispatch happen atomically? When your operations span multiple microservices, you cannot use standard database transactions.

This lesson introduces the Saga pattern as the primary solution for maintaining consistency in distributed systems without relying on the brittle and non-scalable two-phase commit (2PC) protocol.

Understanding the Saga Pattern

In a monolithic application, you wrap multiple database operations in a single DB::transaction. In a distributed environment, your services own separate databases. A "Distributed Transaction" is effectively impossible to enforce across network boundaries.

A Saga is a sequence of local transactions. Each local transaction updates a database and publishes an event or message to trigger the next local transaction in the saga. If a local transaction fails, the saga executes a series of compensating actions that undo the changes made by the preceding local transactions.

The Saga Lifecycle

  1. Execution: The saga coordinator triggers local transactions sequentially.
  2. Failure: If a step fails, the coordinator initiates the rollback sequence.
  3. Compensation: Each completed step is "undone" by a specific compensating action (e.g., if you charged a credit card, the compensation is a refund).

Implementing a Saga in Laravel

For our SaaS platform, let’s consider a common workflow: User Subscription.

  1. Create a User record.
  2. Provision a Cloud Workspace.
  3. Charge the User's credit card.

If the payment fails, we must delete the workspace and remove the user.

Defining the Orchestrator

We’ll use an Orchestrator class to manage the state and flow.

PHP
namespace App\Modules\Billing\Sagas;

use App\Modules\Billing\Actions\CreateUser;
use App\Modules\Billing\Actions\ProvisionWorkspace;
use App\Modules\Billing\Actions\ChargeCustomer;

class SubscriptionSaga
{
    public function execute(array $data)
    {
        try {
            $user = CreateUser::run($data);
            $workspace = ProvisionWorkspace::run($user);
            ChargeCustomer::run($user, $data['amount']);
        } catch (\Exception $e) {
            $this->compensate($user, $workspace ?? null);
            throw $e;
        }
    }

    protected function compensate($user, $workspace)
    {
        if ($workspace) {
            #6A9955">// Logic to delete workspace
        }
        if ($user) {
            #6A9955">// Logic to delete user
        }
    }
}

Worked Example: Compensating Actions

In production, you don't run these synchronously in a controller. You use a state machine or a job-based orchestrator. Let’s refine our ChargeCustomer failure scenario using a dedicated Job.

PHP
#6A9955">// Inside our SubscriptionSaga orchestrator
public function handleFailure($step, $payload)
{
    match($step) {
        'payment' => $this->refundPayment($payload),
        'workspace' => $this->deprovisionWorkspace($payload),
        'user' => $this->deleteUser($payload),
    };
}

Every action must have a corresponding "anti-action." If you cannot guarantee the compensation will succeed (e.g., an API is down), you must queue the compensation to retry until it succeeds, ensuring eventual consistency. Refer to Idempotency keys in databases to ensure that retrying a compensation doesn't result in double-refunds.

Hands-on Exercise

  1. Create a CompensatableAction interface with a compensate() method.
  2. Refactor the SubscriptionSaga to iterate through an array of actions.
  3. If an action throws an exception, loop backward through the completed actions and trigger their compensate() methods.

Common Pitfalls

  • Non-Idempotent Compensations: If your compensation logic (like an API call) isn't idempotent, a network retry might cause a secondary failure. Always check the state of the target resource before acting.
  • Lack of Monitoring: Sagas can get "stuck." Without a dashboard to track the status of a saga (e.g., Pending, Failed, Compensating), you will lose visibility into orphaned data.
  • The "Dirty Read" Problem: Because Sagas are eventually consistent, other parts of your system might see a "Provisioned Workspace" before the "Payment" has actually cleared. Design your UI to handle "Pending" states rather than showing immediate success.

Recap

The Saga pattern replaces distributed transactions by breaking complex flows into discrete, reversible steps. By defining clear compensating actions, you ensure your system returns to a consistent state even when individual services fail. As we move forward, we will look at how to make these events reliable even if the broker goes down.

Up next: Eventual Consistency Patterns — where we implement the Outbox pattern to guarantee event delivery.

Previous lessonIntegrating External Message BrokersNext lesson Eventual Consistency Patterns
Back to Blog

Similar Posts

LaravelJune 28, 20263 min read

Mastering Distributed Locks: Handling Concurrency in Laravel

Learn to use atomic locks and Redis to handle race conditions in distributed systems. Ensure data integrity across your Laravel application's server fleet.

Read more
LaravelJune 28, 20264 min read

Eventual Consistency Patterns: Implementing Outbox and Reconciliation

Master eventual consistency in distributed systems by implementing the Outbox pattern and robust reconciliation tasks to ensure reliable state across services.

Part of the course

Advanced Laravel: Architecture, Scaling & Performance

advanced · Lesson 20 of 57

  1. 1

    Transitioning from MVC to DDD

    3 min
  2. 2

    Defining Bounded Contexts

    3 min
  3. 3

    Implementing Action Classes

    3 min
Read more
LaravelJune 28, 20263 min read

Graceful Degradation: Implementing Circuit Breakers in Laravel

Graceful degradation ensures your Laravel application stays functional when dependencies fail. Learn to implement circuit breakers and robust fallbacks.

Read more
4

Utilizing Data Transfer Objects (DTOs)

3 min
  • 5

    Service Layer Pattern

    4 min
  • 6

    Modular Monolith Structure

    3 min
  • 7

    Querying with Strict Eloquent

    4 min
  • 8

    Advanced Subqueries and Joins

    4 min
  • 9

    Raw Expressions for Performance

    4 min
  • 10

    Advanced Indexing Strategies

    4 min
  • 11

    Database Partitioning Techniques

    4 min
  • 12

    Read/Write Database Splitting

    4 min
  • 13

    Handling Multi-Database Connections

    3 min
  • 14

    Eloquent Caching Strategies

    3 min
  • 15

    Queue Worker Prioritization

    4 min
  • 16

    Unique Job Patterns

    4 min
  • 17

    Rate Limiting Background Jobs

    3 min
  • 18

    Event-Driven Architecture

    4 min
  • 19

    Integrating External Message Brokers

    4 min
  • 20

    Distributed Transactions and Sagas

    3 min
  • 21

    Eventual Consistency Patterns

    4 min
  • 22

    Multi-Layered Caching Strategy

    4 min
  • 23

    Cache Tagging and Invalidation

    4 min
  • 24

    Session Persistence in Clusters

    4 min
  • 25

    High-Availability Infrastructure

    4 min
  • 26

    Zero-Downtime Deployment Pipelines

    4 min
  • 27

    Advanced OAuth2 Implementation

    3 min
  • 28

    JWT and Stateless Security

    4 min
  • 29

    Multi-Tenant Security Isolation

    3 min
  • 30

    Defense Against SSRF

    3 min
  • 31

    Mass Assignment Hardening

    4 min
  • 32

    Automated Security Testing

    3 min
  • 33

    Custom Telemetry Design

    3 min
  • 34

    Distributed Tracing

    4 min
  • 35

    Profiling PHP Execution

    3 min
  • 36

    Memory Management in Long-Running Processes

    4 min
  • 37

    Testing DDD Components

    3 min
  • 38

    Contract Testing

    3 min
  • 39

    Handling Large File Uploads

    3 min
  • 40

    Optimizing Asset Pipelines

    4 min
  • 41

    Database Query Caching Layers

    3 min
  • 42

    Advanced Eloquent Scopes

    4 min
  • 43

    Distributed Locks

    3 min
  • 44

    API Versioning Strategies

    4 min
  • 45

    Database Migration Strategies

    4 min
  • 46

    Handling Webhooks Securely

    3 min
  • 47

    Advanced Logging Patterns

    3 min
  • 48

    Database Indexing for Joins

    4 min
  • 49

    Graceful Degradation

    3 min
  • 50

    Custom Middleware Development

    Coming soon
  • 51

    Database Connection Pooling

    Coming soon
  • 52

    Handling Large Data Exports

    Coming soon
  • 53

    Security Header Configuration

    Coming soon
  • 54

    Database Sharding Concepts

    Coming soon
  • 55

    Real-time Data Synchronization

    Coming soon
  • 56

    Database Deadlock Prevention

    Coming soon
  • 57

    Managing Third-Party API Integrations

    Coming soon
  • View full course