Arquitetura de banco de dados multi-tenant é um design para armazenar muitos clientes em uma camada de dados, isolados por linha, schema ou banco separado. Esses três níveis de isolamento são todo o espaço de decisão — o resto do tema é consequência: como as migrações rodam, quanto custa o restore, onde mora o vizinho barulhento (noisy neighbor) e até onde cada padrão escala.
Principais pontos
| Pergunta | Resposta |
|---|---|
| Os três padrões | Schema compartilhado (tenant ID por linha) · schema por tenant · banco por tenant |
| Vocabulário de nuvem | Os mesmos três: pool · bridge · silo |
| Escolha padrão | Schema compartilhado, a menos que compliance ou escala force o isolamento |
| Tetos práticos | Silo: ~centenas de tenants · bridge: ~1.000 · pool: milhões (com sharding: sem limite) |
| A regra de ferro | Aplique o isolamento no banco, não em cada consulta |
Os três padrões, em SQL
-- Padrão 1 · Schema compartilhado ("pool"): um conjunto de tabelas, tenant em cada linha
CREATE TABLE invoices (
tenant_id uuid NOT NULL,
id bigint GENERATED ALWAYS AS IDENTITY,
total numeric(10,2),
PRIMARY KEY (tenant_id, id) -- tenant na frente: pronto para índice e shard
);
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_rows ON invoices
USING (tenant_id = current_setting('app.tenant')::uuid);
-- Padrão 2 · Schema por tenant ("bridge"): um banco, um namespace para cada
CREATE SCHEMA tenant_acme; -- mesmas tabelas, repetidas por tenant
-- Padrão 3 · Banco por tenant ("silo"): separação física completa
CREATE DATABASE tenant_acme; -- isolamento mais forte; N de tudo
Em um backend gerenciado a mesma garantia se expressa sem SQL: o controle de acesso vive nos próprios dados, então o isolamento vale em todo caminho — API, dashboard ou SDK:
// JavaScript / Node.js — Back4app JS SDK
// The same query for every tenant — ACLs scope results server-side
const query = new Parse.Query('Invoice');
const invoices = await query.find({ sessionToken: user.getSessionToken() });
// Only rows this tenant's role can read come back. No WHERE clause to forget. // Flutter / Dart — Back4app Flutter SDK
// The same query for every tenant — ACLs scope results server-side
final query = QueryBuilder<ParseObject>(ParseObject('Invoice'));
final response = await query.query();
// The session's role decides which rows exist, before results leave the server
if (response.success) {
print('${response.results?.length} invoices visible to this tenant');
} // iOS / Swift — Back4app Swift SDK
// The same query for every tenant — ACLs scope results server-side
let query = Invoice.query()
query.find { result in
if case .success(let invoices) = result {
print("\(invoices.count) invoices visible to this tenant")
}
} // Android / Kotlin — Back4app Android SDK
// The same query for every tenant — ACLs scope results server-side
val query = ParseQuery.getQuery<ParseObject>("Invoice")
query.findInBackground { invoices, e ->
if (e == null) {
Log.d("Billing", "${invoices.size} invoices visible to this tenant")
}
} Schema compartilhado vs. schema por tenant vs. banco por tenant
| Dimensão | Schema compartilhado | Schema por tenant | Banco por tenant |
|---|---|---|---|
| Isolamento | Lógico, por linha | Namespace | Físico |
| Teto de tenants | Milhões | ~Centenas–1.000 | Dezenas–baixas centenas |
| Custo por tenant | O menor | Intermediário | O maior |
| Migrações | Rodam uma vez, atingem todos | Rodam × N, orquestradas | Rodam × N, orquestradas |
| Restore por tenant | Difícil (cópia seletiva) | Moderado | Trivial (restaurar um banco) |
| Vizinho barulhento | Mais exposto | Contido em parte | Eliminado |
| Customização por tenant | A mais difícil | Possível por schema | A mais fácil |
| Onboarding de um tenant | Inserir uma linha | Criar um schema | Provisionar um banco |
Os detalhes que mordem
- Indexação é tenant-first.
tenant_idvai em toda tabela — mesmo onde os joins o fazem parecer redundante — e lidera todo índice composto:(tenant_id, created_at), não o inverso. Toda consulta real tem escopo de um tenant; os índices deveriam ter também. - Migrações se multiplicam com o isolamento. A flexibilidade do silo custa uma camada de orquestração: rastreio de versão por tenant, lógica de retry, detecção de drift. Times subestimam essa linha mais do que qualquer outra da tabela.
- Segurança em nível de linha tem letras miúdas operacionais. Políticas dependem de configurações por conexão, que interagem com os modos de connection pooling — defina o tenant por transação e teste o pooler. Audite também quais roles fazem bypass de RLS; caminhos de superusuário são o buraco clássico.
- Aritmética de conexões. Um pool por banco-de-tenant esgota conexões rápido; designs de schema compartilhado dividem um único pool — mais uma vantagem silenciosa do padrão pool, que só aparece em escala.
- A assimetria de restore guia o tiering. “Vocês conseguem restaurar só a gente para ontem?” é pergunta de contrato enterprise; se a resposta precisa ser sim, esse tenant pertence a um silo — o que leva direto ao híbrido.
Escala e o estado final híbrido
O caminho de crescimento do padrão pool é o sharding por tenant: vários bancos de schema compartilhado, cada um com uma fatia dos tenants, e um catálogo mapeando tenant → shard (o open-source Citus construiu isso dentro do PostgreSQL, incluindo mover um tenant quente para um nó próprio). Combinado com tiering, isso produz a arquitetura para a qual a maior parte do SaaS maduro converge: o tier grátis em pool, tenants médios em pool entre shards, e os poucos tenants regulados ou enormes em silos — com tenant_id mantido em todo schema, em todo lugar, para que qualquer tenant possa se mover entre tiers sem uma remodelagem. Mobilidade de tenant é a propriedade para projetar no primeiro dia; depois, é quase impossível de retrofitar.
Casos de uso comuns
- SaaS B2B. O caso que define o tema: todo workspace, organização ou time do seu produto é um tenant em um desses padrões.
- Produtos freemium em escala. Milhares de pequenos tenants grátis em pool a custo marginal perto de zero — a economia que torna tiers grátis possíveis.
- Verticais reguladas. Clientes de saúde, finanças e jurídico exigindo silos por contrato — servidos do mesmo código-base via híbrido.
- Agências e plataformas. Uma aplicação servindo muitas organizações clientes, cada uma com a própria fronteira.
- Plataformas internas multi-time. Departamentos como tenants em ferramentas compartilhadas — mesmos padrões, modelo de ameaça mais amigável.
Qual padrão você deveria escolher? Matriz de decisão
| Escolha schema compartilhado quando… | Escolha schema por tenant quando… | Escolha banco por tenant quando… |
|---|---|---|
| Tenants são muitos e pequenos | Tenants se contam às centenas | Tenants são poucos e grandes |
| O custo por tenant precisa se aproximar de zero | Isolamento moderado vale alguma operação | Compliance exige separação física |
| Uma migração deve atualizar todo mundo | Ajustes de schema por tenant são necessários | Restore por tenant é contratual |
| Signup self-service, onboarding instantâneo | Tenants entram em ritmo humano | Cada tenant justifica provisionamento |
| Você vai fazer sharding quando crescer | Você vai limitar a contagem de tenants | Você vai automatizar migrações × N |
E a meta-resposta: escolha por tier, não por empresa — designs híbridos colocam cada tenant no padrão mais barato que satisfaz seus requisitos, e os movem quando os requisitos mudam.
Limitações e trade-offs
- Schema compartilhado: a necessidade mais forte de isolamento aplicado pelo banco — um filtro perdido é uma violação, e é por isso que RLS ou ACLs na camada de dados são inegociáveis, não um hardening opcional.
- Schema por tenant: o meio-termo desajeitado — orquestração de migração como a do silo, isolamento mais fraco que o do silo, e limites de metadados do banco chegando surpreendentemente cedo.
- Banco por tenant: tudo × N — migrações, backups, monitoramento, conexões, custo — além de analytics entre tenants virar um projeto de engenharia de dados.
- Os três: o contexto de tenant invade tudo (consultas, caches, jobs, logs), e mudanças entre padrões saem caras sem disciplina de IDs desde o primeiro dia: identificadores globalmente únicos e colunas
tenant_idem todo lugar, até nos silos.
Dados multi-tenant no Back4app
O Back4app é uma plataforma open-source de Backend as a Service (BaaS) que combina banco de dados gerenciado, APIs REST e GraphQL geradas automaticamente, autenticação, armazenamento de arquivos e funções serverless com Cloud Code. Sua resposta à regra de ferro deste artigo — aplique o isolamento no banco — são ACLs por objeto, papéis por tenant e permissões em nível de classe, verificados pela plataforma em toda requisição de toda superfície, como mostram as abas de código acima. É o padrão de schema compartilhado com o risco da cláusula WHERE removido; o mergulho de engenharia em segurança em nível de linha percorre o padrão completo em um banco de documentos, e a visão no nível de hospedagem do mesmo tema cobre a camada de infraestrutura acima.
Perguntas frequentes
Quais são os três padrões de banco de dados multi-tenant?
Schema compartilhado — um conjunto de tabelas, toda linha carregando um identificador de tenant; schema por tenant — um banco, um namespace separado de tabelas por cliente; e banco por tenant — separação física completa. Guias de arquitetura em nuvem chamam os mesmos três de pool, bridge e silo. Sistemas reais cada vez mais os misturam, distribuindo tenants entre padrões por tamanho e necessidade de compliance.
Banco compartilhado ou banco por tenant — qual escolher?
O default de consenso é schema compartilhado, a menos que algo force o isolamento: requisitos regulatórios, separação contratual de dados, customização pesada por tenant ou tenants grandes o bastante para precisar de recursos próprios. Schema compartilhado maximiza densidade e minimiza operação; bancos por tenant maximizam isolamento e multiplicam todo o resto — migrações, backups, conexões, custo.
Quantos tenants cada padrão aguenta?
Os tetos práticos da experiência de produção: banco por tenant roda confortável até dezenas ou baixas centenas de tenants antes de a operação dominar; schema por tenant chega às centenas, até cerca de mil, antes de metadados e orquestração de migrações estrangularem; schema compartilhado escala a milhares ou milhões de tenants, e o sharding por tenant o estende efetivamente sem limite.
Como impedir que um tenant veja os dados de outro?
Defesa em profundidade, nunca uma cláusula WHERE sozinha. Escopo de tenant na aplicação (middleware ou filtros de ORM) é a primeira camada; segurança em nível de linha aplicada pelo banco é a segunda — políticas que filtram toda consulta pelo tenant atual, independente do que a aplicação esqueceu. Em designs de schema compartilhado, um único filtro perdido é uma violação entre tenants — por isso o próprio banco deve aplicar a fronteira.
Como funcionam migrações de schema em cada padrão?
No schema compartilhado, uma migração atualiza todos os tenants de uma vez — simples, com raio de explosão à altura. Em schema ou banco por tenant, a mesma migração precisa rodar uma vez por tenant: centenas de execuções pedindo orquestração, rastreio de versão e detecção de drift, já que uma execução que falha deixa um tenant em um schema mais antigo. Ferramental de migração é o imposto oculto do isolamento.
Dá para restaurar os dados de um único tenant?
Em banco por tenant, trivialmente — restaure aquele banco para qualquer ponto no tempo sem tocar em ninguém. Em schema compartilhado é genuinamente difícil: o backup contém todo mundo, então você restaura em uma instância paralela e copia seletivamente as linhas do tenant de volta. Restore por tenant é um dos argumentos práticos mais fortes que empresas fazem por tiers de isolamento.
Como indexar um banco multi-tenant de schema compartilhado?
Coloque o identificador de tenant em toda tabela — mesmo onde parece redundante — e lidere os índices compostos com ele, para que todo padrão de consulta se torne tenant-first. Fazer da coluna de tenant a parte inicial da chave primária também deixa os dados pré-arrumados para o sharding por tenant mais tarde, que é o caminho de crescimento padrão.
O que é sharding por tenant?
Dividir um design de schema compartilhado entre vários bancos, com todas as linhas de um tenant vivendo em exatamente um shard e um catálogo mapeando tenants para shards. Preserva a densidade do schema compartilhado enquanto limita o tamanho de qualquer banco individual, e permite mover tenants quentes para shards mais calmos. O preço é o catálogo, o ferramental de rebalanceamento e a perda das consultas triviais entre tenants.