Acervo original preservado
Este conteúdo histórico pode conter propostas ou instruções anteriores ao checkout atual. Consulte Arquitetura para o comportamento documentado e Tutoriais para as ressalvas operacionais.
Ambiente e limites da evidência histórica
Configurações e medições locais não comprovam ambientes corporativos. Propostas e menções históricas a produção não foram verificadas em DEV, STAGING ou PROD. Consulte Ambientes corporativos para o que está confirmado.
Headers Globais por Tenant no Gateway¶
Enriquecimento de Geolocalização — Substituição dos Headers CloudFront¶
1. Contexto¶
1.1 Cenário anterior ao Gateway¶
Antes da introdução do Gateway como ponto central de entrada das APIs, as aplicações clientes recebiam automaticamente, via CDN/Edge (CloudFront), headers de geolocalização derivados do IP de origem da requisição:
CloudFront-Viewer-Country
CloudFront-Viewer-City
CloudFront-Viewer-Latitude
CloudFront-Viewer-Longitude
CloudFront-Viewer-Time-Zone
CloudFront-Viewer-Postal-Code
CloudFront-Viewer-Metro-Code
CloudFront-Viewer-Region
CloudFront-Viewer-Region-Name
X-Forwarded-For
Esses headers eram utilizados por clientes para personalização de experiência, auditoria, regras de negócio, analytics, antifraude, direcionamento regional e observabilidade.
1.2 Regressão causada pelo Gateway¶
Com a introdução do Gateway, esses headers deixaram de ser propagados corretamente ao upstream, gerando quebra de compatibilidade com integrações já existentes e impactando funcionalidades dependentes dessas informações.
Este documento trata da primeira etapa da correção dessa regressão: a reconstrução das funções de geolocalização como defaults globais do Gateway, restaurando a paridade funcional com o que o CloudFront entregava anteriormente — agora de forma configurável por tenant.
1.3 Escopo desta iniciativa¶
Esta proposta foi originalmente desenhada como parte de uma iniciativa mais ampla de rastreabilidade e identificação de origem das chamadas via Gateway (mapeamento rota → backend, correlation ID, ambiente de execução, etc). Dessa iniciativa, duas entregas distintas compartilham a mesma infraestrutura técnica de headers globais e serão tratadas neste documento:
Entrega 1 — Headers globais customizados Permitir que o usuário cadastre, manualmente, headers livres que serão aplicados a todas as requisições do tenant, com valor literal ou referenciando uma função disponível na plataforma.
Entrega 2 — Defaults de geolocalização (substituição do CloudFront) Disponibilizar como defaults pré-configurados as funções de geolocalização equivalentes aos headers que o CloudFront fornecia antes da introdução do Gateway, permitindo que o tenant os habilite conforme sua necessidade.
As duas entregas compartilham a mesma tela (Global Configs), o mesmo catálogo de funções (functions-lib), o mesmo fluxo de cadastro e o mesmo fluxo de resolução em runtime — a diferença está apenas em quem origina o header: o usuário, no caso dos customizados, ou a plataforma, no caso dos defaults.
A capacidade de rastreabilidade ponta a ponta (correlation ID, mapeamento rota → backend, metadados de ambiente) não faz parte desta iniciativa e será tratada separadamente, reaproveitando a mesma infraestrutura de headers globais aqui descrita.
1.4 Objetivo¶
Permitir que o usuário cadastre, através de uma tela de configuração (Global Configs), headers que serão automaticamente injetados em todas as requisições do tenant. Esses headers podem ser de dois tipos:
- Defaults (Entrega 2) — funções pré-definidas de geolocalização, mapeadas 1:1 com os headers que o CloudFront fornecia (ex:
$(Totvs.Gateway.Country),$(Totvs.Gateway.Latitude)), desabilitadas por padrão - Customizados (Entrega 1) — headers livres definidos pelo usuário, com valor literal ou referenciando uma função (ex:
x-origem: $(Totvs.Gateway.Latitude))
Exemplo de configuração de um header customizado:
key: x-origem
value: $(Totvs.Gateway.Latitude) → resolvido em runtime para o valor real
1.5 Mapeamento CloudFront → Gateway Functions (Entrega 2)¶
| Header CloudFront (anterior) | Função Gateway (atual) |
|---|---|
CloudFront-Viewer-Country |
Totvs.Gateway.Country |
CloudFront-Viewer-City |
Totvs.Gateway.City |
CloudFront-Viewer-Latitude |
Totvs.Gateway.Latitude |
CloudFront-Viewer-Longitude |
Totvs.Gateway.Longitude |
CloudFront-Viewer-Time-Zone |
Totvs.Gateway.Time-Zone |
CloudFront-Viewer-Postal-Code |
Totvs.Gateway.Postal-Code |
CloudFront-Viewer-Metro-Code |
Totvs.Gateway.Metro-Code |
CloudFront-Viewer-Region |
Totvs.Gateway.Region |
CloudFront-Viewer-Region-Name |
Totvs.Gateway.Region-Name |
X-Forwarded-For |
Totvs.Gateway.X-Forwarded-For |
Diferente do CloudFront — que injetava esses headers automaticamente em toda requisição sem possibilidade de configuração — o Gateway passa a tratar essas funções como defaults opcionais, desabilitados por padrão, que o tenant habilita conforme sua necessidade. Essa mudança de modelo (automático e implícito → configurável e explícito) é uma decisão deliberada de design, detalhada na seção 7.
2. Problema¶
A resolução de headers globais requer:
- catálogo de funções disponíveis (geolocalização, com transformação prevista para etapas futuras) consistente entre cadastro e execução
- validação do valor cadastrado no momento do save, evitando referência a funções inexistentes
- baixa latência na resolução, já que ocorre no caminho crítico de cada requisição (data plane)
- enriquecimento de dados externos (geolocalização por IP) sem comprometer performance, substituindo o que antes era resolvido na borda pelo CloudFront
- consistência entre o cadastro (control plane) e a aplicação em runtime (data plane)
Ou seja, não é apenas um problema de armazenamento de configuração — é um problema de catálogo de funções compartilhado entre cadastro e execução, combinado com enriquecimento de dados em tempo de requisição que antes era resolvido na borda da rede e agora precisa ser resolvido no Gateway.
3. Opções de Arquitetura¶
3.1 Maven Lib compartilhada + gateway-core absorvendo o cadastro (Recomendado)¶
Descrição
- Catálogo de funções definido como Enum em uma lib Maven (
functions-lib), importada pelogateway-coree pelogateway-builder HeaderValueValidatorna mesma lib garante que o valor cadastrado referencia apenas funções conhecidas- Cadastro de headers globais feito através do
gateway-bff, que delega aogateway-core gateway-corepersiste os headers globais em uma nova collection no banco tenantizado existente- O
gateway-builder, que já consulta ogateway-corepara buscar a rota ativa, passa a receber a rota e os headers globais na mesma chamada - A chamada já é cacheada via Redis no
GatewayCoreAdapterexistente — sem custo adicional de latência
Vantagens
- fonte de verdade única para o catálogo de funções — sem divergência entre o que é cadastrado e o que é resolvido
- validação consistente — a mesma regra valida no cadastro e impede que o builder receba uma função desconhecida
- uma única chamada HTTP no runtime, já coberta pelo cache Redis existente
- sem necessidade de novo serviço no caminho crítico
- nova função adicionada ao Enum não exige migration de dados existentes
- aproveita infraestrutura já validada em produção (
GatewayCoreAdapter, cache Redis, banco tenantizado)
Desvantagens
- acoplamento de build — serviços que importam a lib precisam de deploy coordenado ao atualizar o catálogo
- nova função no catálogo exige deploy, não é editável em runtime
- bug na lib afeta todos os serviços que a importam simultaneamente
Conclusão:
Opção recomendada
3.2 Catálogo de funções no MongoDB (sem lib compartilhada)¶
Descrição
- Catálogo de funções armazenado em uma collection própria (
function-catalog), editável diretamente no banco gateway-builderrealiza duas leituras em runtime: uma para o catálogo, outra para os headers do tenant- Nenhuma lib Maven compartilhada entre os serviços
Vantagens
- admin pode adicionar ou editar funções sem necessidade de deploy
- zero acoplamento de build entre os serviços
- cada serviço evolui de forma totalmente independente
Desvantagens
- duas leituras no caminho crítico do builder, com latência dupla
- risco de inconsistência — uma função editada no catálogo pode quebrar tenants que já a utilizam
- sem compile-time safety — erros de função desconhecida só aparecem em produção
- catálogo no banco não resolve a lógica de resolução — nova função ainda exige código novo no builder
- validação do valor cadastrado depende de consulta ao banco, adicionando latência no save
Conclusão:
Não recomendado — a flexibilidade de editar o catálogo sem deploy não compensa a perda de consistência e o custo de duas leituras no runtime.
3.3 Cache em memória no builder (evolução da opção recomendada)¶
Descrição
- Mantém a
functions-libcomo catálogo (Enum + Validator) - Adiciona uma camada de cache em memória no
gateway-builder, com TTL configurável - O
gateway-global-registryatualiza o cache via TTL ou evento, em vez do builder consultá-lo a cada requisição
Vantagens
- remove qualquer dependência externa do caminho crítico de cada requisição
- latência mínima — resolução inteiramente em memória
- tolerante a falhas — requisições continuam mesmo se o serviço de configuração estiver indisponível
- mantém a lib como fonte de verdade, preservando compile-time safety
Desvantagens
- complexidade de invalidação — mudanças no cadastro podem demorar o TTL para refletir no builder
- consumo de memória proporcional ao número de tenants ativos
- necessidade de estratégia de warmup na inicialização do builder
- consistência eventual — tenant pode ver comportamento desatualizado durante o TTL
Conclusão:
Não recomendado para o momento atual. Como a opção 3.1 já resolve o problema de latência através do cache Redis existente no GatewayCoreAdapter, a complexidade adicional de um cache em memória próprio não se justifica nesta fase. Pode ser reavaliada caso o volume de requisições no futuro exija uma camada adicional de performance.
4. Arquitetura Recomendada¶
Fluxo de cadastro (control plane)¶
- Usuário acessa a tela Global Configs
- Front consulta o
gateway-bff:GET /functions→ lista de funções disponíveis, obtida do Enum dafunctions-libGET /global-config/:tenant→ headers já configurados para o tenant
- Usuário habilita defaults e/ou cadastra headers customizados
- Front envia o estado completo via
gateway-bff:POST /global-config/:tenant
gateway-bffdelega aogateway-coregateway-corevalida o payload usando oHeaderValueValidatorda libgateway-corepersiste na collectionroute-common-configs, no banco tenantizado existente
Fluxo de execução (data plane)¶
-
Request chega:
External DNS → Ingress → gateway-broker → gateway-builder -
gateway-buildersolicita aogateway-corea rota ativa, como já faz hoje -
gateway-coreretorna, na mesma resposta:{ "RouteRevision": { ... }, "CommonConfigs": { ... } } -
A chamada é resolvida via cache Redis já existente no
GatewayCoreAdapter— sem latência adicional -
gateway-builderverifica se algum header habilitado utiliza função de geolocalização -
Se houver, chama o
gateway-ip-resolverpara resolver os dados de IP -
gateway-buildercalcula os headers finais, combinando os headers da rota com osCommonConfigsdo tenant -
Requisição segue o fluxo normal
5. Estratégia de Resolução de Funções¶
5.1 Catálogo na functions-lib¶
| Componente | Função |
|---|---|
GatewayFunction (Enum) |
Fonte de verdade do catálogo — expression, label, description, type |
HeaderValueValidator |
Valida se um valor cadastrado referencia apenas funções conhecidas |
5.2 Funções disponíveis¶
Geolocalização (Entrega 2 — defaults) — substituem diretamente os headers anteriormente fornecidos pelo CloudFront (ver mapeamento na seção 1.5)
Totvs.Gateway.Country
Totvs.Gateway.City
Totvs.Gateway.Latitude
Totvs.Gateway.Longitude
Totvs.Gateway.Time-Zone
Totvs.Gateway.Postal-Code
Totvs.Gateway.Metro-Code
Totvs.Gateway.Region
Totvs.Gateway.Region-Name
Totvs.Gateway.X-Forwarded-For
As mesmas funções de geolocalização ficam disponíveis para uso em headers customizados (Entrega 1) — o usuário pode referenciá-las dentro de um header próprio, por exemplo x-origem: $(Totvs.Gateway.Latitude), em vez de habilitar o default com o nome padrão da função.
Transformação — fora do escopo desta iniciativa, prevista para etapa futura
Gateway.Concat
Gateway.ToUpperCase
Gateway.ToLowerCase
Gateway.Length
O catálogo na functions-lib já contempla ambos os tipos (FunctionType.GEO e FunctionType.TRANSFORM), mas a tela de Global Configs e a validação desta iniciativa tratam apenas das funções de geolocalização.
5.3 Fluxo de resolução em runtime¶
1. gateway-builder recebe RouteRevision + CommonConfigs do gateway-core (via Redis cache)
2. verifica se algum header habilitado usa função de geolocalização
3. se sim, chama gateway-ip-resolver
4. popula o dicionário com os valores resolvidos
5. FieldResolver (já existente) resolve $(...) usando o dicionário
6. HeaderBuilder monta os headers finais
6. Modelagem de Dados¶
Collection: suggestions-configs¶
Catálogo de funções disponíveis, somente leitura. Populado via seed a partir do Enum da functions-lib. Não tem relação com tenant — é global para todo o Gateway. Usado exclusivamente para montar a listagem de defaults na tela de Global Configs.
{
"_id": "ObjectId",
"key": "Totvs.Gateway.Country",
"description": "País de origem do IP da requisição",
"type": "GEO"
}
Não possui CRUD — nenhum endpoint de escrita é exposto. Qualquer adição de função nova passa pelo Enum da functions-lib e por uma nova seed nesta collection.
Collection: route-common-configs¶
Persistida no banco tenantizado do gateway-core. Documento único por tenant — sem campo tenantId explícito, pois o isolamento já é garantido pela infraestrutura do banco tenantizado. Consultada pelo gateway-builder via lookup junto a route_revisions.
{
"_id": "ObjectId",
"headers": [
{
"key": "Totvs.Gateway.Country",
"value": "$(Totvs.Gateway.Country)"
},
{
"key": "Totvs.Gateway.Latitude",
"value": "$(Totvs.Gateway.Latitude)"
},
{
"key": "x-origem",
"value": "$(Totvs.Gateway.Latitude)"
},
{
"key": "x-app",
"value": "minha-app"
}
],
"createdAt": "2026-04-16T10:00:00Z",
"updatedAt": "2026-04-16T10:00:00Z"
}
Defaults habilitados e headers customizados ficam no mesmo array headers, reaproveitando o KeySpec já utilizado em resource.transform.headers do route_revisions. Não existe separação estrutural entre os dois — a distinção semântica (default vs custom) existe apenas na camada de apresentação (tela de Global Configs) e no fluxo de cadastro, não no banco.
Quando o usuário habilita um default na tela, um POST adiciona um KeySpec em headers com key e value iguais à expressão da função (ex: key: "Totvs.Gateway.Country", value: "$(Totvs.Gateway.Country)"). Quando desabilita, o KeySpec é removido do array.
Regras obrigatórias¶
Defaults (vêm do suggestions-configs via toggle)
- a presença do
KeySpecno arrayheaderssignifica que está habilitado — ausência implica desabilitado - nenhum campo
enabledé persistido, evitando necessidade de migration ao adicionar novas funções ao catálogo - o
valuede um default é sempre a própria expressão da função:value = "$(Totvs.Gateway.<key>)"
Customs
keydeve ser único dentro do tenantvaluepode ser literal, ou referenciar uma função$(Totvs.Gateway.*)ou$(input.*)- validado pelo
HeaderValueValidatorno momento do save — apenas funções$(Totvs.Gateway.*)desconhecidas são rejeitadas
Array headers (geral)
- a ordem dos elementos não tem impacto no runtime — o
gateway-builderprocessa todos independente de posição keydeve ser único dentro do array — não é possível ter dois headers com a mesma chave
7. Decisão de Design¶
gateway-core absorve o cadastro e a leitura¶
Inicialmente avaliado manter um serviço dedicado (gateway-global-registry) operando sobre um banco global não tenantizado, no mesmo padrão já utilizado para o DNS Alias.
Após validação, identificou-se que a tenantização dos dados é indiferente para esse domínio — diferente do DNS Alias, que precisa ser resolvido antes da identificação do tenant, os headers globais já são consultados em um momento da requisição onde o tenant já está identificado.
Diante disso, a decisão foi consolidar os headers globais no gateway-core, aproveitando:
- a infraestrutura de cache Redis já existente no
GatewayCoreAdapter - a chamada que o
gateway-builderjá realiza para buscar a rota ativa - o banco tenantizado já estabelecido para as demais configurações de rota
Essa decisão elimina a necessidade de um serviço adicional no caminho crítico, mantendo o número de dependências externas do gateway-builder inalterado em relação ao cenário atual.
Resolução de função, não reescrita de payload¶
A resolução de $(Totvs.Gateway.*) ocorre através do FieldResolver já existente no gateway-builder, que resolve tanto headers da rota quanto headers globais utilizando o mesmo dicionário e o mesmo mecanismo de placeholder.
Isso evita duplicação de lógica de resolução entre headers de rota e headers globais.
8. Riscos e Mitigações¶
| Risco | Mitigação |
|---|---|
| Nova função no catálogo exige deploy coordenado | Processo de release definido entre lib, core e builder |
| Bug na lib afeta múltiplos serviços simultaneamente | Versionamento semântico rigoroso e testes de contrato |
| Aumento de latência ao chamar gateway-ip-resolver | Chamada condicional — apenas quando função GEO está habilitada |
| Inconsistência entre cadastro e resolução | Validação compartilhada via HeaderValueValidator |
| Crescimento do catálogo sem necessidade real de runtime | Avaliação de cache em memória (opção 3.3) caso volume justifique |
| Clientes com integrações dependentes dos headers CloudFront permanecem impactados até a habilitação manual dos defaults | Comunicação ativa do mapeamento CloudFront → Gateway Functions (seção 1.5) e suporte na migração dos tenants afetados |
9. Critérios de Aceite por Entrega¶
Entrega 1 — Headers globais customizados¶
- Usuário deve conseguir cadastrar um header com
keyevaluelivres valuedeve aceitar tanto valor literal quanto referência a função ($(Totvs.Gateway.*)ou$(input.*))- Cadastro deve ser validado pelo
HeaderValueValidator, rejeitando funções$(Totvs.Gateway.*)desconhecidas - Headers customizados devem ser aplicados a todas as requisições do tenant, em todas as rotas
- Deve ser possível editar e remover um header customizado já cadastrado
Entrega 2 — Defaults de geolocalização (substituição do CloudFront)¶
- Todos os defaults devem estar disponíveis na tela de Global Configs, desabilitados por padrão
- Usuário deve conseguir habilitar individualmente cada default
- Cada default habilitado deve resolver corretamente o valor de geolocalização via
gateway-ip-resolver - O nome de cada header default deve seguir o padrão
Totvs.Gateway.*, consistente com o mapeamento da seção 1.5 - Defaults não habilitados não devem gerar nenhuma chamada ao
gateway-ip-resolver, preservando performance
10. Conclusão¶
A definição de headers globais por tenant é um problema de catálogo de funções compartilhado entre cadastro e execução, não apenas de persistência de configuração.
Esta iniciativa compreende duas entregas que compartilham a mesma infraestrutura: a possibilidade de o usuário cadastrar headers customizados livremente, e a disponibilização de defaults de geolocalização que restauram a paridade funcional com os headers que o CloudFront fornecia antes da introdução do Gateway. A diferença central em relação ao modelo anterior do CloudFront é que a propagação deixa de ser automática e passa a ser explícita — o tenant decide quais funções de geolocalização deseja habilitar, e pode complementar com seus próprios headers customizados.
A melhor abordagem é:
- Manter o catálogo de funções como Enum em uma lib Maven compartilhada, já preparado para suportar funções de transformação em etapas futuras
- Consolidar cadastro e leitura no
gateway-core, aproveitando o cache Redis já existente - Resolver as funções no
gateway-builderatravés doFieldResolverjá existente - Chamar o
gateway-ip-resolverapenas quando necessário, mantendo o impacto de performance mínimo - Disponibilizar as mesmas funções de geolocalização tanto como defaults quanto para uso dentro de headers customizados
Evitar:
- serviço adicional no caminho crítico do data plane
- catálogo de funções fora do controle de versionamento de código
- duplicação de lógica de resolução entre headers de rota e headers globais
- expansão de escopo para funções de transformação ou rastreabilidade ponta a ponta nesta iniciativa