Estrutura Hierárquica
Como o KRAT ONE organiza contas, times e clientes — e onde exatamente vive a fronteira de isolamento que separa os dados de um tenant do outro.
Os três níveis
Cada nível tem um papel distinto. Confundir os três é a origem da maioria dos bugs de multi-tenancy.
Papel de cada nível
| Nível | Tabela | Responsabilidade | Isolamento | Exemplo |
|---|---|---|---|---|
| Account | account | Cobrança e plano. Um Stripe Customer, uma assinatura. | — | "Agência Aurora Ltda" |
| Território | workspace | Unidade de trabalho e fronteira de segurança. RLS ativa aqui. | RLS | "Aurora · Time SP" |
| Conquista | client | Marca/negócio gerenciado. Dimensão dentro do território. | client_id | "Pizzaria do João" |
| Recursos | contact, deal… | Todo dado de negócio carrega workspace_id + client_id. | RLS | contatos, campanhas… |
Onde as pessoas se conectam
Usuários são identidades globais; o vínculo com o território é a membership.
app_user global
A pessoa existe uma vez no sistema (e-mail único). Isso faz SSO e "convidar por e-mail" funcionarem limpo entre territórios.
membership N:N
Conecta um user a um workspace com um role e um type (interno/externo). Um user pode estar em vários territórios com papéis diferentes.
O escopo fino abaixo do território (quais Conquistas cada membro enxerga) é a tabela client_access — detalhada em Estrutura de Acessos.
Isolamento no banco (RLS)
Não confiamos só no código — o Postgres recusa dados de outro tenant.
-- a cada request, o gateway resolve o território ativo e injeta: SET LOCAL app.workspace_id = '…'; -- toda tabela de negócio: ALTER TABLE contact ENABLE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation ON contact USING ( workspace_id = current_setting('app.workspace_id')::uuid );