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.