No desenvolvimento de aplicações SaaS (Software as a Service), o conceito de Multitenancy (Multilocação) é fundamental. Ele se refere a uma arquitetura onde uma única instância de um software atende a vários clientes (tenants). O grande desafio técnico reside em como isolar os dados de cada cliente de forma segura e eficiente no banco de dados.

​Existem três abordagens principais para implementar essa segregação:

​1. Database per Tenant (Isolamento Total) Link para o cabeçalho

​Cada cliente possui seu próprio banco de dados físico. É a forma mais segura de isolamento.

  • ​Vantagens: Segurança máxima, facilidade para restaurar backups individuais e escalabilidade vertical personalizada.

  • ​Desvantagens: Alto custo operacional e dificuldade para atualizar o esquema (migrates) em centenas de bancos simultaneamente.

​2. Schema per Tenant (Isolamento Lógico) Link para o cabeçalho

​Os clientes compartilham o mesmo banco de dados, mas cada um tem seu próprio Schema (como ocorre no PostgreSQL ou instâncias separadas no SQL Server).

  • ​Vantagens: Equilíbrio entre segurança e custo. É mais fácil de gerenciar que múltiplos bancos, mas mantém uma barreira lógica clara.

​* Desvantagens: Ainda exige gestão de múltiplas migrações de esquema.

​3. Shared Database, Shared Schema (Isolamento por Linha) Link para o cabeçalho

​Todos os dados de todos os clientes residem na mesma tabela. A diferenciação é feita através de uma coluna identificadora (ex: tenant_id).

  • ​Vantagens: Menor custo e extrema facilidade para manutenção e atualizações globais.

  • ​Desvantagens: Risco de “vazamento” de dados se uma consulta esquecer o filtro do ID, e o banco de dados pode se tornar gigantesco rapidamente.

​Exemplo Prático: Implementando Shared Schema (PHP/Laravel) Link para o cabeçalho

​Uma das formas mais didáticas de ver o Multitenancy em ação é através do Global Scopes. No Laravel, podemos garantir que qualquer consulta à tabela de produtos filtre automaticamente pelo cliente logado.

​1. A Migração da Tabela: Link para o cabeçalho

Schema::create('products', function (Blueprint $table) {
    $table->id();
    $table->unsignedBigInteger('tenant_id'); // O divisor de águas
    $table->string('name');
    $table->decimal('price', 10, 2);
    $table->timestamps();
});

2. O Global Scope (A Camada de Segurança): Link para o cabeçalho

Este código garante que, internamente, o SQL executado sempre contenha um WHERE tenant_id = X.

namespace App\Models\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class TenantScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        // Supõe-se que o ID do tenant está na sessão ou resolvido pelo domínio
        if (session()->has('tenant_id')) {
            $builder->where('tenant_id', session('tenant_id'));
        }
    }
}

3. No Model: Link para o cabeçalho

protected static function booted()
{
    static::addGlobalScope(new TenantScope);
}

Com isso, ao executar Product::all(), o sistema retornará apenas os produtos do cliente atual, sem que o desenvolvedor precise escrever o filtro manualmente em cada parte do código.