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 52 of the Intermediate Laravel: Real-World Application Patterns course
LaravelJune 26, 20264 min read

Implementing Multi-Tenancy: Architecting Isolated Laravel Applications

Learn to implement robust multi-tenancy in Laravel. Master tenant scoping and data isolation strategies to build secure, scalable, and maintainable SaaS platforms.

LaravelMulti-tenancyArchitectureEloquentSaaSphpbackend

Previously in this course, we explored testing with test doubles to isolate our logic from external dependencies. In this lesson, we shift our focus to architectural isolation: implementing multi-tenancy.

Multi-tenancy is the architectural backbone of any SaaS application. It allows a single instance of your software to serve multiple customers (tenants), each with their own isolated data. Failing to design for this from the ground up often leads to "leaky" data, where a user from Tenant A accidentally views or modifies data belonging to Tenant B.

Designing for Multi-Tenancy

When architecting for multi-tenancy, your primary goal is data isolation. You must decide how to partition the data before writing your first migration. There are three common approaches:

StrategyDescriptionComplexity
Database-per-tenantSeparate DB for every client.High
Schema-per-tenantShared DB, separate Postgres schemas.Medium
Column-based (Shared)Shared DB, every table has a tenant_id.Low

For most Laravel applications, the Column-based approach is the standard starting point. It is cost-effective, easy to back up, and works seamlessly with standard Eloquent relationships. We will focus on this pattern, as it forces us to be explicit about our query scoping.

Implementing Tenant Scoping

To implement column-based multi-tenancy, every model related to a tenant (like our Project or Task models) must include a tenant_id column. The challenge is ensuring that every query automatically filters by this ID without manually adding ->where('tenant_id', ...) everywhere.

We achieve this using Global Scopes.

Step 1: The Tenant Trait

Create a BelongsToTenant trait that automatically applies a scope to any model using it.

PHP
namespace App\Traits;

use App\Models\Scopes\TenantScope;
use Illuminate\Support\Facades\Auth;

trait BelongsToTenant
{
    protected static function bootBelongsToTenant()
    {
        static::addGlobalScope(new TenantScope);

        static::creating(function ($model) {
            if (Auth::check()) {
                $model->tenant_id = Auth::user()->tenant_id;
            }
        });
    }
}

Step 2: The Global Scope

The TenantScope class intercepts the query builder to inject the constraint.

PHP
namespace App\Models\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Support\Facades\Auth;

class TenantScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        if (Auth::check()) {
            $builder->where('tenant_id', Auth::user()->tenant_id);
        }
    }
}

Managing Data Isolation

Even with global scopes, you must be vigilant. A common risk is the "Context Switch" problem—where a background job or an API command runs without a logged-in user, potentially returning empty results or, worse, all results if you aren't careful.

When dealing with API architecture and request context propagation, always ensure the tenant_id is explicitly set in the container or via a service class, rather than relying solely on Auth::user().

The Project Board Evolution

In our project board application, we now need to ensure every Project is tied to a tenant_id.

  1. Migration: Add unsignedBigInteger('tenant_id')->index() to your projects and tasks tables.
  2. Model: Apply the BelongsToTenant trait to the Project and Task models.
  3. Controller: Because we use global scopes, your index methods like Project::all() will now automatically return only the projects belonging to the authenticated user's tenant.

Hands-on Exercise

  1. Create a Tenant model and a migration that adds tenant_id to your existing users, projects, and tasks tables.
  2. Implement the BelongsToTenant trait as shown above.
  3. Write a feature test that attempts to access a project belonging to a different tenant_id. Assert that a 404 Not Found or 403 Forbidden response is returned, proving the scope is working.

Common Pitfalls

  • Forgetting Global Scopes in Jobs: If you dispatch a background job, the Auth::user() will be null. You must pass the tenant_id into the job constructor and manually set it in the job's handle() method.
  • Seeding/Testing: When running tests, global scopes will hide data from your factories. Use withoutGlobalScope(TenantScope::class) when verifying data in your test assertions.
  • The "Cross-Tenant" Leak: Never assume that an ID is globally unique. Always validate that the requested resource belongs to the current tenant before performing an update or delete.

Recap

Multi-tenancy requires a strict adherence to data isolation. By leveraging Global Scopes, we ensure that our application logic is "tenant-aware" by default. While this approach simplifies development, always remember that background processes and console commands require explicit tenant context to prevent data leaks.

Up next: We will discuss Refactoring Legacy Code to ensure our growing codebase remains maintainable as we add features.

Previous lessonTesting with Test DoublesNext lesson Refactoring Legacy Code
Back to Blog

Similar Posts

LaravelJune 28, 20263 min read

Multi-Tenant Security: Eloquent Isolation and Tenant-Aware Scopes

Master multi-tenant security by implementing robust row-level isolation in Laravel. Learn to build tenant-aware Eloquent scopes that prevent cross-tenant leaks.

Read more
LaravelJune 28, 20264 min read

Advanced Eloquent Scopes: Building Complex, Reusable Query Filters

Master advanced Eloquent scopes to encapsulate complex business logic, chain query filters, and maintain clean, expressive models in your Laravel SaaS platform.

Part of the course

Intermediate Laravel: Real-World Application Patterns

intermediate · Lesson 52 of 58

  1. 1

    Architecting for Maintainability

    3 min
  2. 2

    Implementing the Service Layer

    3 min
  3. 3

    Repository Pattern Fundamentals

    3 min
Read more
LaravelJune 28, 20264 min read

Mass Assignment Hardening: Securing Eloquent Models

Mass assignment is a critical security vulnerability where attackers inject unauthorized fields into your database. Learn to harden your Laravel models today.

Read more
  • 4

    Project Board Domain Modeling

    3 min
  • 5

    Advanced Eloquent Scopes and Accessors

    4 min
  • 6

    Service-Oriented Task Management

    3 min
  • 7

    REST API Fundamentals with Sanctum

    3 min
  • 8

    Resource Controllers and API Responses

    3 min
  • 9

    Handling API Validation and Form Requests

    3 min
  • 10

    Implementing Middleware for API Security

    4 min
  • 11

    Database Transactions for Data Integrity

    3 min
  • 12

    Error Handling and Global Exceptions

    3 min
  • 13

    Introduction to Laravel Events and Listeners

    3 min
  • 14

    Asynchronous Processing with Queues

    4 min
  • 15

    Job Chaining and Batching

    3 min
  • 16

    Feature Testing Fundamentals

    4 min
  • 17

    Mocking Services and Repositories in Tests

    3 min
  • 18

    Testing Events and Jobs

    3 min
  • 19

    Database Factories and Seeding

    3 min
  • 20

    API Versioning Strategies

    4 min
  • 21

    Advanced Request Filtering and Sorting

    3 min
  • 22

    Handling File Uploads in REST APIs

    3 min
  • 23

    Real-time Notifications with Broadcasting

    3 min
  • 24

    Using Observers for Model Lifecycle Hooks

    3 min
  • 25

    Implementing Policies for Authorization

    3 min
  • 26

    Customizing Authentication Guards

    3 min
  • 27

    Rate Limiting API Endpoints

    4 min
  • 28

    Eloquent Performance Optimization

    4 min
  • 29

    Caching Strategies for Performance

    4 min
  • 30

    Using Traits for Code Reuse

    3 min
  • 31

    Advanced Dependency Injection with Service Providers

    3 min
  • 32

    Command Line Tools with Artisan

    3 min
  • 33

    Scheduled Tasks and Cron Jobs

    3 min
  • 34

    Integrating Third-Party Services

    3 min
  • 35

    Handling Webhooks

    3 min
  • 36

    Logging and Monitoring

    3 min
  • 37

    Database Migrations Best Practices

    3 min
  • 38

    Advanced Testing: Integration Tests

    4 min
  • 39

    Testing API Authentication

    4 min
  • 40

    Code Quality and Static Analysis

    3 min
  • 41

    Project Structure for Large Applications

    3 min
  • 42

    Environment and Configuration Management

    3 min
  • 43

    Deploying Laravel Applications

    4 min
  • 44

    Database Indexing Strategies

    4 min
  • 45

    Using Value Objects

    4 min
  • 46

    Strategy Pattern for Business Rules

    3 min
  • 47

    Advanced Queue Monitoring

    3 min
  • 48

    Building a Search API

    3 min
  • 49

    Handling Concurrency and Race Conditions

    4 min
  • 50

    API Documentation with OpenAPI

    3 min
  • 51

    Testing with Test Doubles

    3 min
  • 52

    Implementing Multi-Tenancy

    4 min
  • 53

    Refactoring Legacy Code

    4 min
  • 54

    Using Middleware for Feature Flags

    3 min
  • 55

    Building Reusable Packages

    4 min
  • 56

    Performance Profiling

    3 min
  • 57

    Secure API Design

    3 min
  • 58

    Event Sourcing Concepts

    4 min
  • View full course