Multi-tenancy com segurança no Lino
Multi-tenancy permite que uma única aplicação atenda vários clientes, empresas ou organizações mantendo dados e permissões isolados. No Lino, tenant é tratado como parte do fluxo da aplicação: o recurso gera a base de tenancy, resolve o tenant pelo subdomínio, host ou slug via ITenantContext e expõe o usuário autenticado pelo JWT via IUserContext.
O JWT também carrega o TenantId para o qual o usuário foi autenticado. O Lino confronta esse tenant com o tenant resolvido para a requisição, então a documentação desta página foca no que você precisa decidir depois que essa base existe: modelagem de entidades, permissões por organização e uso correto das abstrações geradas.
Adicionando suporte a tenant
Use o recurso de tenant quando o sistema for SaaS ou quando dados de clientes diferentes precisarem conviver na mesma aplicação sem vazamento entre contextos.
lino feature tenant add
O assistente gera a base para cadastro de tenants, associação de usuários, resolução do tenant atual, propagação pelo contexto da aplicação e integração com autenticação. O ITenantContext representa o tenant resolvido pelo subdomínio, host ou slug, enquanto o IUserContext representa o usuário autenticado pelo JWT, incluindo o TenantId para o qual ele foi autenticado.
Depois da geração, revise quais entidades realmente pertencem a um tenant e quais são globais. Essa revisão é parte da modelagem do produto: multi-tenancy não é apenas criar uma tabela de tenants, mas decidir quais regras, telas, permissões, dados e operações dependem do tenant atual validado pelo Lino.
- Tenant-scoped: pedidos, produtos de um cliente, configurações de empresa, clientes de um CRM ou qualquer dado que pertence a uma organização específica.
- Global: catálogo público da plataforma, planos, recursos internos de operação, configurações administrativas ou dados usados antes de escolher um tenant.
- Híbrido: dados globais com customização por tenant, que exigem modelagem explícita para evitar duplicação incorreta ou vazamento de regra.
Em um monólito modular, normalmente o módulo de tenancy é dono da entidade original de tenant. Outros módulos, como catálogo ou CRM, não devem acessar essa entidade diretamente. Quando precisam conhecer tenant, eles podem manter shadow entities com os campos mínimos necessários, alimentadas por eventos de integração como criação ou atualização de tenant.
Tenant context
Com o recurso de tenant, o Lino centraliza a resolução do tenant atual e disponibiliza esse valor pelo ITenantContext. Esse contexto representa o tenant resolvido a partir do subdomínio, host ou slug da requisição, incluindo informações como TenantId e slug quando o tenant foi encontrado.
Em fluxos autenticados, o IUserContext lê o usuário a partir do JWT. Esse JWT também possui o TenantId para o qual o usuário foi autenticado. O Lino confronta o tenant do IUserContext com o tenant do ITenantContext, evitando que a regra de negócio confie em contextos divergentes.
Nas funcionalidades geradas ou adicionadas depois, prefira sempre essas abstrações em vez de reler claims manualmente ou receber um TenantId arbitrário em cada handler. Isso mantém commands, queries, páginas e integrações usando a mesma fonte de verdade e preserva os mecanismos de segurança já implementados pelo Lino.
Background Jobs, consumidores de eventos e integrações também precisam de contexto. Um job que processa produtos, clientes ou usuários tenant-scoped deve receber explicitamente qual tenant está sendo processado, porque não existe uma requisição HTTP ativa de onde ITenantContext e IUserContext possam ser inferidos automaticamente.
Entidades com escopo de tenant
Entidades tenant-scoped carregam o identificador do tenant e participam dos filtros de consulta. Com isso, o fluxo comum não precisa lembrar manualmente de aplicar TenantId em todas as queries; a funcionalidade deve usar o contexto validado e os filtros gerados pelo Lino.
- Inclua tenant em aggregates que pertencem a uma organização específica.
- Configure índices considerando tenant e campos consultados com frequência.
- Use
ITenantContexteIUserContextem commands e queries em vez de receber tenant como autoridade isolada. - Em imports e jobs, defina explicitamente qual tenant está sendo processado.
Uma entidade tenant-scoped pertence a um tenant. Se uma entidade parece pertencer a vários tenants, trate isso como uma decisão de modelagem: talvez ela seja global, talvez deva existir uma cópia por tenant, ou talvez falte uma entidade intermediária que represente o relacionamento. Um produto, cliente ou usuário também pode ter significado diferente em módulos diferentes; nesses casos, uma shadow entity com poucos campos pode ser mais correta do que compartilhar a entidade original.
Também diferencie referência de ownership. Um produto no catálogo pode referenciar o tenant, mas isso não significa que o catálogo controla o ciclo de vida do tenant. O módulo de tenancy continua dono da criação, ativação, domínio, branding e modo de isolamento; o catálogo mantém apenas os dados necessários para filtrar e validar seu próprio fluxo.
- Índices: combine
TenantIdcom campos usados em busca, ordenação e unicidade por tenant. - Comandos: aplique a operação no tenant atual validado quando a entidade for tenant-scoped.
- Queries: mantenha paginação, filtros e contadores no mesmo contexto validado pelo Lino.
- Eventos: inclua contexto suficiente para consumidores atualizarem projeções sem consultar o produtor indevidamente.
Isolamento de dados
Isolamento pode ser feito por coluna, schema ou banco separado. A escolha depende de custo, volume, requisitos contratuais e capacidade operacional.
| Estratégia | Vantagens | Quando usar |
|---|---|---|
| Coluna TenantId | Mais simples e barata | Boa escolha inicial para muitos SaaS, combinada com filtros e contexto do Lino. |
| Schema por tenant | Isolamento intermediário | Útil quando há exigências maiores de separação sem chegar a banco por tenant. |
| Banco por tenant | Isolamento forte | Indicado quando contrato, volume ou operação justificam custo e automação maiores. |
Para a maioria dos SaaS em estágio inicial, coluna por tenant com filtros globais e contexto centralizado é um ponto de partida pragmático. O Lino ajuda nessa base; sua revisão deve confirmar que os aggregates tenant-scoped carregam o tenant correto e que as consultas novas continuam usando o tenant atual validado pelas abstrações geradas.
Em serviços modulares, isolamento de tenant e isolamento de módulo são preocupações diferentes. O módulo Catalog pode ter uma tabela de produtos com TenantId; o módulo CRM pode ter clientes com TenantId; e o módulo Tenancy continua sendo dono do cadastro original de tenants. A presença do mesmo identificador não autoriza acesso direto entre módulos.
Roles e permissões por tenant
Em SaaS, um usuário pode ser administrador em um tenant e operador em outro. O Lino gera a base para trabalhar com vínculo usuário-tenant, e as permissões das funcionalidades devem ser avaliadas dentro do contexto ativo validado pelo framework.
Também existem permissões administrativas fora de tenant. Uma tela de operação da plataforma pode ser acessível apenas no contexto do sistema, enquanto páginas como produtos e clientes podem exigir tenant ativo. Essa separação deixa claro quando a ação é global e quando ela depende do tenant resolvido no ITenantContext e do usuário autenticado no IUserContext.
- Modele papéis por tenant quando permissões variam por organização.
- Use o contexto atual nas verificações de autorização das páginas, commands e queries.
- Mantenha claims enxutas, carregando no token apenas o que precisa participar do fluxo autenticado.
- Revogue ou atualize tokens quando permissões críticas mudarem.
| Contexto | Exemplo | Como tratar |
|---|---|---|
| Sistema | Administração da plataforma, planos, tenants e configurações globais. | Executar fora do escopo de um tenant específico. |
| Tenant | Produtos, clientes, pedidos, usuários e roles daquele tenant. | Usar o tenant atual validado pelo Lino. |
| Ambos | Algumas funções de identidade ou gestão que existem tanto no sistema quanto no tenant. | Declarar no caso de uso qual contexto está sendo usado. |
JWT e segurança de tenant
Quando o recurso de tenant está ativo, o Lino usa o JWT como fonte do contexto do usuário autenticado. O IUserContext lê as claims do usuário e o TenantId do token, sem transformar o JWT em uma cópia do banco de dados.
- Mantenha emissor, audience, assinatura e expiração configurados corretamente.
- Use o tenant validado pelo Lino em vez de aceitar uma troca manual baseada apenas em header, rota ou payload da tela.
- Evite claims grandes demais; tokens inchados dificultam operação e renovação.
- Registre logs de acesso com usuário, tenant e operação quando o fluxo exigir auditoria.
Em aplicações com subdomínios, como acme.dev.localhost e globex.dev.localhost, o ITenantContext representa o tenant resolvido para a requisição. O IUserContext representa o usuário autenticado e o tenant presente no JWT. Esses dois contextos são confrontados pelo Lino para evitar que um token emitido para um tenant seja usado em outro contexto de tenant.
A autorização também continua contextual. A permissão de visualizar produtos, por exemplo, vale dentro do tenant em que foi concedida e para o qual o usuário foi autenticado. Ao criar funcionalidades novas, use as permissões e os contextos já disponíveis em vez de criar atalhos que recebam um TenantId avulso e ignorem o fluxo autenticado.
