Multi-tenancy e ownership¶
Tenant seleciona contexto de dados; ownership indica quem possui um objeto; autorização decide quem pode acessá-lo. Essas três funções não são equivalentes. Uma database por tenant é separação de namespace de armazenamento, não prova de instância dedicada, credencial exclusiva ou autorização cross-tenant.
Seleção de banco¶
| Caminho | Implementação |
|---|---|
| BFF administrativo | Extrai companyId do JWT e propaga x-tenant-id nas chamadas contextuais; exceções públicas têm resolução própria |
| Core, Templates, IP Policy, Rate Limit, Importer, Storage, OpenAPI Doc, Tunnel | Anotações/contexto e injeção de MongoCollection<T> via biblioteca tenant. Nomes físicos de collections explicitados por @MongoEntity quando presentes |
| Security | MultiTenantFactory chama mongoClient.getDatabase(tenantId); filtro de contexto possui fallback default |
| Leitura de modelo proprietário | GatewayTemplateOwnerRepository usa getDatabase(ownerTenantId) e collections explícitas de modelos/revisões |
| Operation por tenant | OperationRepositoryByTenant resolve database/collection para execução fora do request original |
| Registry | Database global explícita gateway-globals; filtra owner/visibilidade conforme operação |
| Storage público | Registro global resolve owner; TenantOwnerFileMetadataReader lê metadados no contexto do dono |
| Scheduler | Database da aplicação configurada, sem seleção geral por header; rate_limit_activities.tenantId é contexto lógico de quota. HTTP callbacks podem transportar tenant nos headers |
| Dispatcher | Itera databases Mongo visíveis não sistêmicas; nome da database dirige claim/atualização da outbox |
O algoritmo geral de nomes e isolamento da biblioteca tenant não está no código-fonte dos microserviços disponível neste workspace. Por isso o inventário usa “Tenant/biblioteca” em vez de atribuir a todos o mesmo nome físico. Chamadas explícitas de leitura cross-tenant são registradas como tais.
Fontes: TenantResolver BFF, transporte BFF, factory Security, leitura owner, Operation por tenant, arquivos do owner, config Scheduler, Dispatcher Mongo.
Políticas de outro tenant¶
%%{init: {"theme":"base","themeVariables":{"fontFamily":"Arial, sans-serif","fontSize":"16px","lineColor":"#64748b","primaryTextColor":"#172b4d"},"flowchart":{"htmlLabels":true,"useMaxWidth":true,"nodeSpacing":28,"rankSpacing":38,"padding":14,"curve":"linear","subGraphTitleMargin":{"top":12,"bottom":16}},"sequence":{"useMaxWidth":true,"actorMargin":28,"width":145,"height":45,"diagramMarginX":32,"diagramMarginY":12,"messageMargin":24,"wrap":true,"mirrorActors":false},"themeCSS":".edgeLabel, .edgeLabel p {background-color:var(--diagram-surface)!important;color:var(--diagram-text)!important}\n.cluster rect {fill:var(--diagram-group)!important;stroke:var(--diagram-line)!important}\n.cluster-label span, .cluster-label text {color:var(--diagram-text)!important;fill:var(--diagram-text)!important}\n.flowchart-link, .actor-line {stroke:var(--diagram-line)!important}\nmarker path {fill:var(--diagram-line)!important;stroke:var(--diagram-line)!important}\n.actor {fill:var(--diagram-domain)!important;stroke:var(--diagram-domain-border)!important}\ntext.actor, .messageText, .loopText, .labelText {fill:var(--diagram-text)!important}\n.messageLine0, .messageLine1, .loopLine {stroke:var(--diagram-line)!important}\n.labelBox {fill:var(--diagram-surface)!important;stroke:var(--diagram-line)!important}\n.note {fill:var(--diagram-async)!important;stroke:var(--diagram-async-border)!important}\n.noteText {fill:var(--diagram-text)!important}\n.nodeLabel, .nodeLabel p, .nodeLabel span {color:var(--diagram-text)!important}\n.node .nodeLabel, .node .nodeLabel p {font-size:17px;font-weight:600;line-height:1.4;text-align:center}\n.nodeLabel small {display:inline-block;margin-top:4px;font-size:15px;font-weight:400;line-height:1.4;color:var(--diagram-description)!important}\ntext.actor, text.actor tspan {font-size:17px;font-weight:600}\n.node.entry rect, .node.entry polygon, .node.entry path, .node.entry circle {fill:var(--diagram-entry)!important;stroke:var(--diagram-entry-border)!important}\n.node.domain rect, .node.domain polygon, .node.domain path, .node.domain circle {fill:var(--diagram-domain)!important;stroke:var(--diagram-domain-border)!important}\n.node.security rect, .node.security polygon, .node.security path, .node.security circle {fill:var(--diagram-security)!important;stroke:var(--diagram-security-border)!important}\n.node.store rect, .node.store polygon, .node.store path, .node.store circle {fill:var(--diagram-store)!important;stroke:var(--diagram-store-border)!important}\n.node.async rect, .node.async polygon, .node.async path, .node.async circle {fill:var(--diagram-async)!important;stroke:var(--diagram-async-border)!important}\n.node.external rect, .node.external polygon, .node.external path, .node.external circle {fill:var(--diagram-external)!important;stroke:var(--diagram-external-border)!important}\n text.actor tspan, .loopText tspan, .labelText tspan, .messageText tspan, .noteText tspan {fill:var(--diagram-text)!important}\n\n"}}%%
flowchart TB
config["Revisão Core<br/><small>Tenant consumidor</small>"]:::store
lookup["Resolução de política<br/><small>ID + targetTenantId</small>"]:::security
owner["Security / IP Policy<br/><small>Dados do tenant proprietário</small>"]:::store
config -->|Herança / sourceTenantId| lookup
lookup -->|Consulta no owner| owner
classDef security stroke-width:1.5px;
classDef store stroke-width:1.5px;
A projeção de Discovery escolhe a configuração conforme herança: INHERITED usa gateway; OWN/OVERWRITE usa rota; NONE desativa o vínculo aplicável. IDs locais prevalecem quando presentes; referências de origem usam sourceTenantId. Não se unem automaticamente listas de IDs locais e de origem.
O contrato emitido identifica authentication.{id,targetTenantId} e ipPolicies[].{id,targetTenantId}. O cache de objetos do Checker usa o owner no prefixo; o cache de rotas continua agrupado pelo consumidor. Isso evita confundir objetos de tenants diferentes no lookup, mas não comprova autorização para compartilhá-los. Uma lista persistida de IP Policies possui um owner de origem para o conjunto, não um owner por item arbitrário.
Fontes: projeção Core, cache Security, cache IP, decisão de acesso.
Dados globais e materializações¶
Catálogo contém owner e revisão do modelo; aquisição cria configuração no consumidor. Registry não contém o documento completo das rotas. Publicação de arquivo registra owner sem duplicar binário. Aliases Redis e documentos OpenAPI são materializações com atualização própria. Outbox/Inbox identificam tenant no contexto da operação/mensagem, sem tornar a entrega uma transação entre bancos.
Referências globais, snapshots e acesso por serviço estão no mapa de relacionamentos. As lacunas de autorização e invalidação transversal estão em Análises de dados; nenhuma topologia ou política corporativa foi inferida dos manifests locais.