Multi-Inquilino
Regras
- Isolamento em nível de linha (padrão): adicione uma coluna
tenantId a toda tabela com escopo de inquilino — aplique via middleware ou RLS (Segurança em Nível de Linha)
- Esquema por inquilino: use quando os inquilinos precisam de esquemas personalizados ou isolamento estrito de dados — operações mais complexas, melhor isolamento
- Roteamento por subdomínio: extraia o inquilino de
{tenant}.app.com — resolva no middleware, defina tenantId no contexto da requisição
- Domínios personalizados: aponte CNAME para seu aplicativo, identifique o inquilino pelo cabeçalho
Host — use uma tabela domains mapeando domínios personalizados para IDs de inquilino
- Configuração por inquilino: armazene tema (logotipo, cores), flags de funcionalidades e limites em uma tabela
tenant_settings — armazene em cache na memória com TTL
- Padrão RLS (Postgres):
ALTER TABLE posts ENABLE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation ON posts USING (tenant_id = current_setting('app.tenant_id'))
- Middleware: extraia o inquilino do subdomínio/cabeçalho, defina no contexto, aplique a todas as consultas ao banco de dados — nunca deixe uma consulta ser executada sem escopo de inquilino
- Particionamento de dados: para tabelas grandes, particione por
tenantId — melhora a performance de consultas e permite backup/restauração por inquilino
Padrão de Isolamento em Nível de Linha
function getTenantFromHost(host: string): string {
const subdomain = host.split(".")[0];
return subdomain;
}
prisma.$use(async (params, next) => {
if (TENANT_MODELS.includes(params.model)) {
params.args.where = { ...params.args.where, tenantId: context.tenantId };
}
return next(params);
});
Roteamento por Subdomínio (Middleware Next.js)
export function middleware(request: NextRequest) {
const host = request.headers.get("host") ?? "";
const tenant = host.split(".")[0];
const response = NextResponse.next();
response.headers.set("x-tenant-id", tenant);
return response;
}
Evite
- Consultas sem escopo de inquilino — uma única
WHERE tenantId = ? ausente vaza dados entre inquilinos
- Codificar o inquilino fixo no cliente — resolva a partir do subdomínio/domínio no servidor
- Falta de índice em
tenantId — toda tabela com escopo de inquilino precisa de um índice composto começando com tenantId
- Misturar dados de inquilinos em caches compartilhados — namespace as chaves de cache com
{tenantId}:{chave}