
Multi-tenancy em SaaS: quando e como implementar
Todo SaaS começa com uma pergunta técnica que parece simples mas define anos de arquitetura: como separar os dados de um cliente dos dados de outro? Essa é a essência do multi-tenancy — a capacidade de um sistema servir múltiplos "inquilinos" (tenants) com total isolamento entre eles, usando a mesma infraestrutura.
Fazer isso errado tem consequências sérias: um bug de lógica pode vazar dados de uma empresa para outra, uma query pesada de um cliente pode derrubar o serviço para todos, e uma migração mal feita pode exigir downtime de horas. Fazer certo, desde o início, é uma das decisões de arquitetura mais rentáveis que um founder técnico pode tomar.
Existem três modelos principais, cada um com trade-offs claros de isolamento, custo e complexidade operacional.
Banco Compartilhado com tenant_id: Simples e Perigoso
O modelo mais comum em MVPs é o mais arriscado em produção: todas as tabelas do banco ficam em um único schema, e cada linha tem uma coluna tenant_id que identifica a qual organização ela pertence.
CREATE TABLE projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
name TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_projects_tenant ON projects(tenant_id);
A lógica de isolamento fica inteiramente na aplicação: todo SELECT, INSERT e UPDATE precisa incluir o tenant_id do usuário autenticado. O problema é óbvio — um único esquecimento no código expõe dados de outros tenants. Isso não é hipotético: é um dos vetores de vazamento de dados mais comuns em SaaS.
Além do risco de segurança, há o problema de performance. Como todos os tenants compartilham as mesmas tabelas, um cliente com volume alto de dados compete diretamente com outros. Índices parciais ajudam, mas não eliminam o problema em escala.
Quando faz sentido: MVPs com menos de 50 tenants, volume baixo de dados, sem requisitos de compliance rigorosos (LGPD/GDPR com isolamento físico) e quando a equipe de engenharia ainda está definindo o modelo de dados.
Schema por Tenant: Equilíbrio entre Isolamento e Custo
Neste modelo, cada tenant tem seu próprio schema dentro do mesmo banco de dados PostgreSQL. As tabelas têm estrutura idêntica, mas ficam fisicamente separadas por namespace.
-- Criação automática ao cadastrar novo tenant
CREATE SCHEMA tenant_acme;
CREATE TABLE tenant_acme.projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Na aplicação, o schema é definido por sessão
SET search_path TO tenant_acme;
O isolamento lógico é real: uma query sem o SET search_path correto simplesmente não enxerga os dados do tenant. O risco de vazamento por esquecimento na camada de aplicação é dramaticamente menor.
A operação também fica mais simples: backups por tenant, migrações independentes, análise de query por cliente específico. Ferramentas como pg_dump aceitam o parâmetro --schema para exportar apenas os dados de um tenant.
O custo: PostgreSQL tem um limite prático de schemas por banco (em torno de alguns milhares antes de degradação de performance no catálogo do sistema). Para SaaS com dezenas de milhares de tenants pequenos, esse modelo começa a criar pressão.
| Característica | Banco compartilhado | Schema por tenant |
|---|---|---|
| Isolamento | Lógico (código) | Lógico (banco) |
| Risco de vazamento | Alto | Baixo |
| Custo operacional | Baixo | Médio |
| Escalabilidade (tenants) | Alta | Média (até ~5.000) |
| Backup por tenant | Complexo | Simples |
| Migração de schema | Uma vez | Por tenant |
Banco por Tenant: Máximo Isolamento para Enterprise
Clientes enterprise — especialmente em setores regulados como saúde, financeiro e governo — frequentemente exigem isolamento físico de dados: um banco de dados dedicado, às vezes em uma instância própria. Isso é o modelo banco por tenant.
A complexidade operacional aumenta consideravelmente: provisionamento dinâmico de bancos, connection pooling (PgBouncer ou RDS Proxy se tornando obrigatórios), monitoramento multiplicado pelo número de tenants, e custo de infraestrutura que cresce linearmente.
Em troca, você tem:
- Conformidade com requisitos de residência de dados (data residency)
- Possibilidade de oferecer SLA diferenciado por cliente
- Isolamento total de performance
- Backup e restore granular por conta
# Exemplo de infraestrutura com Terraform para provisionamento dinâmico
resource "aws_db_instance" "tenant" {
for_each = var.enterprise_tenants
identifier = "saas-tenant-${each.key}"
engine = "postgres"
engine_version = "15.4"
instance_class = each.value.db_tier
allocated_storage = each.value.storage_gb
db_name = "tenant_db"
username = "app_user"
password = random_password.tenant[each.key].result
skip_final_snapshot = false
}
Quando faz sentido: Clientes enterprise com ticket acima de R$ 2.000/mês, requisitos explícitos de isolamento no contrato, setor de saúde ou financeiro, ou quando o cliente representa mais de 20% da receita total e qualquer vazamento seria catastrófico.
Row Level Security como Alternativa no PostgreSQL
Uma abordagem cada vez mais adotada combina o modelo de banco compartilhado com Row Level Security (RLS) do PostgreSQL — tornando o isolamento uma garantia do banco, não da aplicação.
-- Habilitando RLS na tabela
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
-- Política: usuário só vê linhas do próprio tenant
CREATE POLICY tenant_isolation ON projects
USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
-- Na conexão, antes de qualquer query:
SET app.current_tenant_id = '550e8400-e29b-41d4-a716-446655440000';
Com RLS ativo, mesmo uma query sem filtro de tenant_id retorna apenas os dados do tenant configurado na sessão. O banco se torna o guardião da segregação — não o código da aplicação.
O limite do RLS é performance: políticas complexas podem degradar queries em tabelas muito grandes. E a configuração incorreta do SET ainda pode ser um vetor de problema se feita no ORM de forma errada. Mas para a maioria dos SaaS em crescimento, RLS com banco compartilhado oferece o melhor equilíbrio entre segurança e custo operacional.
Conclusão com CTA
A escolha do modelo de multi-tenancy não é permanente, mas migrar entre eles em produção sem downtime é trabalhoso. A estratégia mais comum é começar com banco compartilhado + RLS, migrar para schema por tenant quando a base de clientes chega a algumas centenas, e oferecer banco dedicado como opção premium para enterprise.
O que nunca deve ser adiado é a definição do tenant_id como coluna obrigatória desde o primeiro commit — e a adoção de RLS desde o MVP. Refatorar isso depois, com dados em produção, é uma das operações mais custosas em engenharia de SaaS.
Na SystemForge, todo SaaS que construímos já nasce com a arquitetura de multi-tenancy correta para o estágio do produto: RLS com banco compartilhado no MVP, schema por tenant na fase de crescimento, banco dedicado no tier enterprise. A fundação técnica certa poupa meses de retrabalho. Fale com a gente para discutir a arquitetura do seu produto.
Precisa de Desenvolvimento SaaS?
A SystemForge constrói plataformas SaaS escaláveis do zero até o deploy.
Saiba mais →Precisa de ajuda?


