Multi Tenancy no Laravel: Como Construir um SaaS Multiempresa
Multi Tenancy no Laravel: Como Construir um SaaS Multiempresa

Multi Tenancy no Laravel: Como Construir um SaaS Multiempresa

A criação de um SaaS multiempresa (multi-tenancy) com Laravel pode parecer um desafio, mas o framework oferece diversas abordagens para implementação. Neste artigo, vamos explorar o conceito de multi-tenancy, as principais estratégias de implementação e como estruturar um sistema eficiente e seguro.

O Que é Multi Tenancy?

Multi-tenancy (ou multiempresa) é um modelo de arquitetura onde um único aplicativo atende a várias organizações (tenants), garantindo isolamento adequado dos dados. Isso permite que um SaaS sirva múltiplos clientes sem precisar manter várias instâncias do mesmo sistema.

Estratégias de Multi Tenancy no Laravel

O Laravel suporta diferentes abordagens para multi-tenancy. As principais são:

  1. Banco de Dados Compartilhado (Single Database, Scoped by Tenant)
    • Todos os tenants compartilham o mesmo banco de dados.
    • As tabelas incluem uma coluna company_id para separar os dados.
    • Recomendado para SaaS que não exigem alto isolamento de dados.
  2. Banco de Dados Separado por Cliente (Multiple Databases)
    • Cada tenant tem seu próprio banco de dados.
    • O Laravel muda dinamicamente a conexão do banco de acordo com o tenant.
    • Oferece maior isolamento, mas pode ser mais complexo de gerenciar.
  3. Schema Separado (Multiple Schemas in a Single Database)
    • Cada tenant tem seu próprio esquema dentro do mesmo banco de dados.
    • Um bom equilíbrio entre isolamento e eficiência de recursos.

Implementando Multi Tenancy no Laravel

1. Estruturando o Banco de Dados

Se optar pelo banco de dados compartilhado, inclua uma coluna company_id em todas as tabelas relevantes:

Schema::table('users', function (Blueprint $table) {
    $table->unsignedBigInteger('company_id');
    $table->foreign('company_id')->references('id')->on('companies');
});

2. Configurando o Middleware para Identificar o Tenant

Podemos criar um middleware para identificar o tenant a partir do subdomínio ou cabeçalho:

namespace App\Http\Middleware;

use Closure;
use App\Models\Company;

class IdentifyTenant
{
    public function handle($request, Closure $next)
    {
        $host = $request->getHost();
        $company = Company::where('domain', $host)->firstOrFail();
        
        app()->instance('tenant', $company);
        
        return $next($request);
    }
}

Adicione esse middleware no Kernel.php:

protected $middleware = [
    \App\Http\Middleware\IdentifyTenant::class,
];

3. Aplicando Filtro Global para Restringir os Dados

Podemos usar Global Scopes para restringir automaticamente os dados por tenant:

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)
    {
        if ($tenant = app('tenant')) {
            $builder->where('company_id', $tenant->id);
        }
    }
}

Adicione esse escopo ao seu modelo:

use App\Models\Scopes\TenantScope;

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

Agora, todas as consultas serão automaticamente filtradas pelo company_id!

4. Configurando Conexões Dinâmicas para Múltiplos Bancos

Se cada tenant tiver seu próprio banco, podemos configurar a conexão dinamicamente:

DB::purge('tenant');

config(['database.connections.tenant' => [
    'driver' => 'mysql',
    'host' => $tenant->db_host,
    'database' => $tenant->db_name,
    'username' => $tenant->db_user,
    'password' => $tenant->db_password,
]]);

DB::reconnect('tenant');

E então, basta definir a conexão nos modelos:

class User extends Model
{
    protected $connection = 'tenant';
}

5. Utilizando Pacotes para Facilitar a Implementação

Se preferir uma solução pronta, o pacote Spatie Laravel Multitenancy simplifica a configuração:

composer require spatie/laravel-multitenancy

Ele permite configurar a identificação de tenants e mudança dinâmica de banco de dados com poucas linhas de código.

Conclusão

Implementar multi-tenancy no Laravel exige planejamento, mas o framework oferece diversas ferramentas para tornar essa tarefa mais simples. Seja optando por um banco compartilhado, múltiplos esquemas ou bancos separados, a escolha depende da complexidade e necessidades do seu SaaS.

Se estiver construindo um SaaS multiempresa, considere o isolamento de dados, segurança e escalabilidade desde o início para evitar problemas futuros!

Gostou do artigo? Compartilhe com sua equipe e comece a construir seu SaaS multi-tenancy com Laravel! 🚀