Arquitetura SaaS Backend

Como criar uma plataforma SaaS multi-tenant do zero

Multi-tenancy é a base de qualquer SaaS escalável — mas escolher a estratégia errada custa caro depois.

N
Neryx Digital Architects
30 de janeiro de 2026
10 min de leitura
160 profissionais leram
Categoria: Arquitetura Público: Times de engenharia e produto Etapa: Aprendizado

O que é multi-tenancy e por que importa

Multi-tenancy é a capacidade de uma única instância de software servir múltiplos clientes — chamados de "tenants" — com isolamento de dados, configurações e, dependendo da estratégia, de recursos de computação.

É o que permite que um SaaS como o Slack sirva simultaneamente a Apple, uma startup de 5 pessoas e uma ONG — cada uma com seus próprios usuários, dados e configurações — sem que esses dados se misturem.

A escolha da estratégia de multi-tenancy é uma das decisões mais importantes na arquitetura de um SaaS. Tomada errada no início, ela gera retrabalho massivo quando o produto cresce. Vamos destrinchar cada abordagem.

Os três modelos de isolamento

Modelo 1 — Banco de dados separado por tenant

Cada cliente tem seu próprio banco de dados. É o modelo de maior isolamento: uma falha ou vazamento em um banco não afeta outros tenants.

Tenant A → banco_a (PostgreSQL instance)
Tenant B → banco_b (PostgreSQL instance)
Tenant C → banco_c (PostgreSQL instance)

Vantagens: isolamento total de dados, compliance facilitado (LGPD, GDPR, HIPAA), backup e restore por tenant, possibilidade de migrar tenants para regiões diferentes.

Desvantagens: custo alto (cada banco tem overhead fixo), operação complexa (migrations precisam rodar em N bancos), difícil para analytics cross-tenant.

Quando usar: SaaS enterprise com requisitos de compliance rigorosos (saúde, financeiro, jurídico), quando clientes pagam por isolamento dedicado, ou quando tenants têm volumes de dados muito diferentes.

Modelo 2 — Schema separado por tenant (mesmo banco)

Um único banco de dados PostgreSQL, mas cada tenant tem seu próprio schema. As tabelas de cada tenant ficam em tenant_a.users, tenant_b.users, etc.

-- Mesmo banco, schemas separados
SELECT * FROM tenant_abc.orders WHERE status = 'pending';
SELECT * FROM tenant_xyz.orders WHERE status = 'pending';

Vantagens: migrations por schema são mais simples que por banco, isolamento lógico satisfatório para a maioria dos casos, custo menor que banco separado.

Desvantagens: ainda complexo para analytics cross-tenant, limite prático de schemas por banco (~alguns milhares), backup granular por tenant exige trabalho.

Quando usar: produto B2B com clientes médios a grandes, quando o número de tenants é limitado (até alguns centenas/milhares), equilíbrio entre isolamento e custo operacional.

Modelo 3 — Tabelas compartilhadas com coluna tenant_id

Todos os tenants compartilham as mesmas tabelas. Cada linha tem uma coluna tenant_id que identifica o dono do registro. É o modelo mais simples de implementar e o mais comum em SaaS de volume alto.

CREATE TABLE orders (
  id          UUID PRIMARY KEY,
  tenant_id   UUID NOT NULL REFERENCES tenants(id),
  customer    VARCHAR(255),
  total       DECIMAL(10,2),
  status      VARCHAR(50),
  created_at  TIMESTAMP DEFAULT NOW()
);

— Índice composto obrigatório CREATE INDEX idx_orders_tenant ON orders (tenant_id, created_at DESC);

Vantagens: migrations simples (uma vez), analytics cross-tenant trivial, custo mínimo, escala para milhões de tenants.

Desvantagens: isolamento apenas lógico (bug de software pode vazar dados entre tenants), Row Level Security obrigatório para segurança, compliance mais trabalhoso.

Quando usar: SaaS B2C ou B2B SMB com muitos tenants pequenos, produto early-stage que precisa de velocidade, quando os dados dos tenants são de natureza similar e de baixo risco regulatório.

Implementando Row Level Security no PostgreSQL

No modelo de tabelas compartilhadas, o RLS do PostgreSQL é uma camada de segurança essencial — ele garante que, mesmo que o código da aplicação tenha um bug, o banco bloqueará acesso a dados de outros tenants.

-- Habilita RLS na tabela
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

— Política: usuário só vê registros do seu tenant CREATE POLICY tenant_isolation ON orders USING (tenant_id = current_setting(‘app.current_tenant_id’)::UUID);

— Na aplicação (ASP.NET Core), antes de cada query: await db.ExecuteAsync( “SET app.current_tenant_id = @tenantId”, new { tenantId = currentTenant.Id } );

Com RLS ativo, mesmo que o código faça SELECT * FROM orders sem filtro, o PostgreSQL automaticamente adiciona o filtro por tenant. É uma rede de segurança que opera no nível do banco.

Resolvendo o problema das migrations

Independente do modelo escolhido, migrations em SaaS multi-tenant exigem estratégia. Para o modelo de schema separado, você precisa rodar a migration em todos os schemas existentes:

// Exemplo em C# com Dapper
public async Task RunMigration(string sql)
{
    var tenants = await _db.QueryAsync<string>(
        "SELECT schema_name FROM tenants_registry"
    );
foreach (var schema in tenants)
{
    await _db.ExecuteAsync(
        $"SET search_path TO {schema}; {sql}"
    );
}

}

Para o modelo de banco separado, ferramentas como Flyway e Liquibase suportam execução em múltiplos datasources. O importante é que migrations sejam sempre retrocompatíveis (nunca delete uma coluna sem um período de depreciação) para permitir deploys sem downtime.

Roteamento por tenant na aplicação

A aplicação precisa identificar em qual tenant o usuário está operando. As estratégias mais comuns:

// Estratégia 1: subdomínio
// empresa.seuapp.com → tenant_id da "empresa"

// Estratégia 2: path // seuapp.com/t/empresa/dashboard

// Estratégia 3: JWT claim (mais comum para APIs) // Token JWT contém { “tenant_id”: “uuid-aqui” }

// Middleware ASP.NET Core para extrair tenant do JWT public class TenantMiddleware { public async Task InvokeAsync(HttpContext context) { var claim = context.User.FindFirst(“tenant_id”); if (claim != null) { var tenantId = Guid.Parse(claim.Value); context.Items[“TenantId”] = tenantId;

        // Injeta no contexto do banco para RLS
        await _db.SetTenantAsync(tenantId);
    }
    await _next(context);
}

}

Plano de escalabilidade

O modelo ideal pode evoluir com o crescimento do produto:

  • Early stage (0-100 tenants): tabelas compartilhadas com tenant_id + RLS. Foco em velocidade.
  • Crescimento (100-1.000 tenants): avaliar schema por tenant para clientes enterprise que exigem isolamento.
  • Scale (1.000+ tenants): sharding por tenant_id para distribuir carga entre múltiplos bancos. Tenants grandes ganham banco dedicado.

Essa evolução gradual evita over-engineering no início — você não precisa construir o sistema para 100.000 tenants quando tem 10.

Conclusão

Não existe modelo certo de multi-tenancy — existe o modelo certo para o seu momento. Para a maioria dos SaaS em estágio inicial, tabelas compartilhadas com RLS é a escolha mais pragmática. Para produtos enterprise com requisitos de compliance, schema ou banco separado pode ser obrigatório.

A Neryx tem experiência construindo plataformas SaaS multi-tenant do zero, do design de schema à implementação de middlewares de isolamento. Se você está iniciando ou evoluindo um produto SaaS, a consultoria inicial é gratuita.

Precisa desenhar a próxima fase com menos retrabalho?

Fazemos discovery técnico para mapear riscos, arquitetura-alvo e sequência de execução antes de investir pesado.

Solicitar Discovery

Newsletter

Receba artigos como este no seu e-mail

Conteúdo técnico sobre arquitetura de software, .NET, IA e gestão de produto. Sem spam.