Toda aplicação SaaS atende múltiplos clientes. A arquitetura que determina como os dados desses clientes coexistem—ou não—é chamada de multi-tenancy. Acerte, e sua aplicação escala elegantemente de 10 clientes para 10.000. Erre, e você vai vazar dados entre clientes (catastrófico) ou bater em um muro de escalabilidade que exige uma reescrita completa (caro).
Multi-tenancy não é uma funcionalidade que você adiciona. É uma decisão arquitetural fundamental que molda o design do seu banco de dados, seu modelo de segurança, sua estratégia de deploy e seu pricing. Na Meld, construímos sistemas multi-tenant para clientes de diversas indústrias—da plataforma de aviação do AeroCopilot com suas 173 tabelas de banco de dados a sistemas empresariais que nosso CTO arquitetou na Avenue Code para organizações como Banco Itaú e Santander.
Aqui estão as três abordagens, quando usar cada uma e como implementá-las sem as armadilhas.
O Que Multi-Tenancy Realmente Significa
Um tenant é uma organização cliente. Em um SaaS B2B, cada empresa que se cadastra é um tenant. Em um SaaS B2C com funcionalidades de equipe, cada time ou workspace é um tenant.
Multi-tenancy significa que múltiplos tenants compartilham a mesma infraestrutura de aplicação. A alternativa—single tenancy, onde cada cliente obtém seu próprio deploy—funciona para software empresarial on-premise mas não escala para a economia de SaaS.
A questão não é se deve ser multi-tenant. Se você está construindo SaaS, você está construindo multi-tenant. A questão é como isolar os dados de cada tenant.
Abordagem 1: Banco de Dados Compartilhado, Schema Compartilhado
Como funciona: Todos os tenants compartilham um banco de dados e as mesmas tabelas. Cada tabela tem uma coluna tenant_id. Cada consulta inclui um filtro WHERE tenant_id = ?.
-- Tabela de usuários CREATE TABLE users ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL REFERENCES tenants(id), email VARCHAR(255) NOT NULL, name VARCHAR(255), UNIQUE(tenant_id, email) ); -- Cada consulta filtra por tenant SELECT * FROM users WHERE tenant_id = 'abc-123' AND email = 'user@example.com';
Vantagens:
- Mais simples de implementar. Um banco de dados, um schema, um deploy. Padrões ORM padrão funcionam direto.
- Menor custo operacional. Um banco de dados para fazer backup, monitorar e manter. Connection pooling é simples.
- Mais fácil de atualizar. Migrações de schema são aplicadas uma vez para todos os tenants simultaneamente.
- Melhor utilização de recursos. Tenants pequenos não desperdiçam recursos com infraestrutura dedicada.
Desvantagens:
- Risco de isolamento de dados. Um filtro
tenant_idfaltando vaza dados entre tenants. Este é o bug mais comum e mais perigoso em multi-tenancy de schema compartilhado. - Problema do vizinho barulhento. Um tenant executando um relatório pesado desacelera todos os tenants compartilhando o mesmo banco de dados.
- Teto de escalabilidade. Eventualmente, um banco de dados não é suficiente. Fazer sharding de um banco compartilhado é complexo.
- Limitações de conformidade. Algumas indústrias e regiões exigem separação física de dados. Schema compartilhado não satisfaz isso.
Quando usar: A maioria dos MVPs. Se você tem menos de 1.000 tenants, sem requisitos regulatórios de isolamento de dados e precisa entregar rápido, schema compartilhado é a escolha pragmática. É por onde a maioria dos produtos SaaS começa, e muitos nunca precisam sair.
Detalhe crítico de implementação: Use Row-Level Security (RLS) no nível do banco de dados, não apenas filtragem no nível da aplicação. As políticas de RLS do PostgreSQL garantem que, mesmo se seu código de aplicação perder um filtro, o próprio banco de dados previne acesso cruzado entre tenants:
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON users
USING (tenant_id = current_setting('app.current_tenant')::uuid);
Essa abordagem de defesa em profundidade é inegociável. Bugs de aplicação acontecem. Isolamento no nível do banco de dados garante que eles não se tornem vazamentos de dados.
Abordagem 2: Banco de Dados Compartilhado, Schemas Separados
Como funciona: Todos os tenants compartilham um servidor de banco de dados, mas cada tenant obtém seu próprio schema de banco de dados (namespace). As tabelas são idênticas em estrutura mas isoladas por schema.
-- Usuários do Tenant A SELECT * FROM tenant_a.users WHERE email = 'user@example.com'; -- Usuários do Tenant B SELECT * FROM tenant_b.users WHERE email = 'user@example.com';
Vantagens:
- Isolamento mais forte que schema compartilhado. Sem filtro
tenant_idpara esquecer. Os dados de cada tenant são estruturalmente separados. - Customização por tenant possível. Você pode adicionar colunas ou índices específicos por tenant sem afetar outros.
- Exportação de dados mais fácil. Fazer dump dos dados de um único tenant é uma operação no nível do schema, não uma exportação filtrada.
- Custo operacional moderado. Ainda é um servidor de banco de dados, mas com separação lógica.
Desvantagens:
- Complexidade de migração de schema. Cada migração deve ser executada em cada schema de tenant. Com 500 tenants, são 500 execuções de migração.
- Gerenciamento de conexões. Cada schema pode exigir sua própria conexão ou configuração de pool de conexões.
- Consultas cross-tenant são mais difíceis. Relatórios entre todos os tenants exigem queries UNION entre schemas ou um banco de dados analítico separado.
- Limites do banco de dados. Alguns bancos de dados têm limites práticos no número de schemas (PostgreSQL lida com milhares bem; outros não).
Quando usar: Quando você precisa de isolamento de dados mais forte que o schema compartilhado oferece mas não quer a sobrecarga operacional de bancos de dados separados. Bom para SaaS B2B com clientes conscientes de conformidade nos setores de saúde, finanças ou jurídico. Nosso CTO usou essa abordagem com sucesso para sistemas multi-tenant empresariais onde clientes como o Santander exigiam separação lógica de dados mas não mandavam separação física.
Consideração de implementação: Automatize a criação de schemas e migração. Quando um novo tenant se cadastra, seu pipeline de provisionamento deve criar o schema, executar todas as migrações e popular dados padrão automaticamente. Gerenciamento manual de schemas não escala além de 20 tenants.
Abordagem 3: Bancos de Dados Separados
Como funciona: Cada tenant obtém seu próprio banco de dados. Isolamento físico completo.
Vantagens:
- Isolamento máximo. Nenhuma possibilidade de vazamento de dados entre tenants no nível do banco de dados.
- Escalabilidade independente. Tenants pesados obtêm bancos maiores. Tenants leves obtêm menores. Recursos correspondem à demanda.
- Backup e restauração por tenant. Restaure os dados de um único tenant sem afetar mais ninguém.
- Satisfação de conformidade. Separação física de dados satisfaz os requisitos regulatórios mais rigorosos, incluindo leis de residência de dados que exigem que dados permaneçam em regiões geográficas específicas.
- Janelas de manutenção independentes. Atualize o banco de dados de um tenant sem downtime para outros.
Desvantagens:
- Maior complexidade operacional. Gerenciar centenas ou milhares de bancos de dados requer automação sofisticada.
- Maior custo. Cada banco de dados consome computação, armazenamento e recursos de backup independentemente de quão pequeno o tenant é.
- Analytics cross-tenant requerem ETL. Você precisa de um pipeline de dados para agregar insights entre tenants.
- Gerenciamento de conexões em escala. Sua aplicação precisa rotear requisições para o banco de dados correto, gerenciar pools de conexão por banco e lidar com falhas de nível de banco independentemente.
Quando usar: SaaS empresarial com grandes clientes que exigem isolamento de dados. Indústrias reguladas (saúde, bancário, governo). Cenários onde tenants têm requisitos de escala muito diferentes. Se seu maior cliente tem 10.000x mais dados que o menor, bancos separados permitem dimensionar a infraestrutura adequadamente.
Padrão de implementação: Use um banco de dados de registro de tenants que mapeia identificadores de tenant para strings de conexão do banco de dados. Seu middleware de aplicação resolve o tenant atual (de subdomínio, claim JWT ou chave de API) e roteia a requisição para o banco de dados apropriado:
Requisição → Resolver Tenant → Buscar Conexão → Executar Query → Retornar Resposta
Essa camada de roteamento é a infraestrutura crítica. Construa-a bem, teste-a completamente e monitore-a obsessivamente.
A Abordagem Híbrida: Comece Compartilhado, Gradue Seletivamente
A abordagem mais inteligente para a maioria das startups é híbrida: comece com a Abordagem 1 (schema compartilhado com RLS) e gradue tenants de alto valor para a Abordagem 3 (bancos de dados separados) quando precisarem.
É assim que a arquitetura do AeroCopilot foi projetada. Com 173 tabelas de banco de dados e crescendo, o modelo de dados é complexo o suficiente para que o isolamento de tenants importe desde o primeiro dia. Row-Level Security fornece a linha de base, com a arquitetura projetada para suportar banco-por-tenant para clientes empresariais quando chegar o momento.
A chave é projetar a camada de abstração cedo. Seu código de aplicação nunca deve se conectar a um banco de dados diretamente. Ele deve solicitar uma conexão através de um serviço tenant-aware — um padrão bem suportado por ORMs como Prisma que pode rotear para bancos compartilhados ou dedicados sem mudar a lógica da aplicação:
// O código da aplicação não sabe nem se importa qual banco de dados está usando
const db = await getTenantConnection(tenantId);
const users = await db.user.findMany({ where: { active: true } });
Se getTenantConnection retorna uma conexão filtrada para um banco compartilhado ou uma conexão direta para um banco dedicado é uma decisão de infraestrutura, não de aplicação. Essa separação é o que torna a graduação possível sem reescrever a lógica de negócio.
Padrões de Isolamento de Dados Além do Banco de Dados
Multi-tenancy se estende além do banco de dados. Cada camada da sua stack precisa de consciência de tenant:
Armazenamento de arquivos. Arquivos de tenants devem ser isolados. Use chaves de objeto prefixadas (/tenants/{tenant_id}/uploads/) ou buckets de armazenamento separados. Nunca deixe um ataque de enumeração de URLs expor os arquivos de um tenant para outro.
Cache. Chaves de cache devem incluir o identificador do tenant. Um cache hit para os dados de dashboard do Tenant A nunca deve servir a requisição do Tenant B. Prefixe cada chave de cache: tenant:{id}:dashboard:stats.
Jobs em background. Jobs devem carregar contexto de tenant. Um job que processa faturas deve saber de qual tenant são as faturas e deve se autenticar contra a conexão correta do banco de dados.
Índices de busca. Se você usa Elasticsearch, Algolia ou similar, dados de tenants devem ser isolados no índice. Use aliases filtrados, índices separados ou IDs de documento prefixados por tenant.
Logging. Cada entrada de log deve incluir o identificador do tenant. Ao depurar um problema de produção, você precisa filtrar logs para o tenant afetado instantaneamente.
Considerações de Segurança
Falhas de segurança multi-tenant viram manchete. Algumas práticas inegociáveis:
A resolução do tenant acontece uma vez, na borda. Seu middleware resolve o tenant da requisição (subdomínio, header, JWT) e o define para todo o ciclo de vida da requisição. Nenhuma função mais profunda na stack deve re-resolver o tenant.
Teste acesso cross-tenant explicitamente. Sua suíte de testes deve incluir testes que tentam acessar dados do Tenant B enquanto autenticado como Tenant A. Esses testes devem falhar. Automatize-os. Execute-os no CI. Nunca os pule.
Audite consultas com escopo de tenant. Registre toda consulta de banco de dados que toca dados de tenant. Quando (não se) um cliente perguntar "vocês podem provar que meus dados estão isolados?" você precisa de evidência.
Rate limit por tenant. Um único tenant não deve ser capaz de esgotar a capacidade da sua API. Rate limiting por tenant protege todos os tenants de vizinhos barulhentos e previne abuso.
Escolhendo Sua Abordagem
Use este framework de decisão:
| Fator | Schema Compartilhado | Schema Separado | Banco Separado |
|---|---|---|---|
| Velocidade de implementação | ★★★ | ★★ | ★ |
| Simplicidade operacional | ★★★ | ★★ | ★ |
| Força de isolamento de dados | ★ | ★★ | ★★★ |
| Customização por tenant | ★ | ★★ | ★★★ |
| Eficiência de custo em pequena escala | ★★★ | ★★ | ★ |
| Conformidade regulatória | ★ | ★★ | ★★★ |
Para a maioria dos MVPs, comece com schema compartilhado e RLS. Projete a camada de abstração que permite graduar tenants depois. Entregue rápido, isole adequadamente e evolua a arquitetura conforme sua base de clientes e seus requisitos crescem.
Multi-tenancy feito certo é invisível para seus clientes e sem esforço para seu time. Multi-tenancy feito errado é um vazamento de dados esperando para acontecer. A diferença é tomar essa decisão deliberadamente, com total entendimento dos trade-offs, antes de escrever sua primeira linha de código de aplicação.
Se você está construindo um SaaS e precisa de ajuda para arquitetar multi-tenancy corretamente desde o primeiro dia, é exatamente esse tipo de decisão fundamental que a Meld ajuda fundadores a acertar.
