ADR-001 — Um banco por organização
Data: 2026-08-09 (virada multi-tenant) · Situação: vigente
Contexto
O AppStock nasceu como sistema de uma empresa só, acoplado a um CRM e a um ERP específicos. Ao virar SaaS, era preciso escolher como isolar clientes. As três opções usuais:
- Coluna
org_idem cada tabela — barato de implementar, caro de garantir: basta umWHEREesquecido para vazar dado entre clientes. - Schema por organização — isolamento razoável, um banco só,
search_pathpor conexão. - Banco por organização — isolamento físico.
Decisão
Banco por organização, em dois clusters: app_<slug> (portal) e sync_<slug> (espelho do
ERP), com um appstock_core para o plano de controle.
O DSN de cada banco vive em org_databases, e db/tenants.py resolve a engine por
(org_id, kind). get_db/get_sync_db mantiveram nome e assinatura — o que permitiu virar
cerca de 100 arquivos de rotas e serviços sem tocá-los.
Por quê
- Vazamento cross-org deixa de ser um bug possível e vira um bug impossível. Não existe
WHEREa esquecer: a conexão já é do banco certo. - Isolamento de recurso. Consulta pesada de um cliente esgota o pool dele, não o dos outros —
é por isso que o pool do espelho tem
pool_timeout=10. - Backup, restore e migração de tier viram operações por cliente.
- O espelho pode ser read-only de verdade, com credencial separada.
Consequências
Boas — o isolamento simplificou o produto: a visibilidade por sharing que existia no CRM de origem virou desnecessária na v1 (todo usuário da organização vê a organização). O recorte fino por carteira/equipe voltou como escopo nativo, não como espelho de permissão de terceiro.
Ruins, e são reais:
- N bancos = N × 70 migrations. Toda migration precisa rodar em todo
app_<org>. Hoje isso é feito no provisionamento (ALEMBIC_URLpor subprocess); não existe rotina de “atualizar todas as organizações” — é o próximo problema a aparecer. - Provisionar ficou caro. Criar dois bancos e rodar 70 migrations dentro de uma requisição HTTP é o que trava o flx-onboarding.
- Consulta agregada entre clientes é impossível sem um caminho explícito. Para métricas de produto (quantos clientes, quanto uso) vai precisar de coleta própria.
- Conexões multiplicam. Cada organização ativa mantém até 2 pools de 5+10. Escala bem até uma ordem de grandeza; depois exige pooler externo.
Alternativa descartada e por quê
Schema por organização economizaria bancos, mas mantém um único cluster e um único conjunto de credenciais — o isolamento de recurso e o read-only do espelho ficariam por convenção, não por estrutura. Para um produto que guarda o estoque e o financeiro de empresas concorrentes entre si, “por convenção” não bastava.