Pular para conteúdo

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 pelo gateway-core e pelo gateway-builder
  • HeaderValueValidator na mesma lib garante que o valor cadastrado referencia apenas funções conhecidas
  • Cadastro de headers globais feito através do gateway-bff, que delega ao gateway-core
  • gateway-core persiste os headers globais em uma nova collection no banco tenantizado existente
  • O gateway-builder, que já consulta o gateway-core para buscar a rota ativa, passa a receber a rota e os headers globais na mesma chamada
  • A chamada já é cacheada via Redis no GatewayCoreAdapter existente — 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-builder realiza 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-lib como catálogo (Enum + Validator)
  • Adiciona uma camada de cache em memória no gateway-builder, com TTL configurável
  • O gateway-global-registry atualiza 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)

  1. Usuário acessa a tela Global Configs
  2. Front consulta o gateway-bff:
    • GET /functions → lista de funções disponíveis, obtida do Enum da functions-lib
    • GET /global-config/:tenant → headers já configurados para o tenant
  3. Usuário habilita defaults e/ou cadastra headers customizados
  4. Front envia o estado completo via gateway-bff:
    • POST /global-config/:tenant
  5. gateway-bff delega ao gateway-core
  6. gateway-core valida o payload usando o HeaderValueValidator da lib
  7. gateway-core persiste na collection route-common-configs, no banco tenantizado existente

Fluxo de execução (data plane)

  1. Request chega:

    External DNS → Ingress → gateway-broker → gateway-builder
    
  2. gateway-builder solicita ao gateway-core a rota ativa, como já faz hoje

  3. gateway-core retorna, na mesma resposta:

    {
      "RouteRevision": { ... },
      "CommonConfigs": { ... }
    }
    
  4. A chamada é resolvida via cache Redis já existente no GatewayCoreAdapter — sem latência adicional

  5. gateway-builder verifica se algum header habilitado utiliza função de geolocalização

  6. Se houver, chama o gateway-ip-resolver para resolver os dados de IP

  7. gateway-builder calcula os headers finais, combinando os headers da rota com os CommonConfigs do tenant

  8. 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 KeySpec no array headers significa que está habilitado — ausência implica desabilitado
  • nenhum campo enabled é persistido, evitando necessidade de migration ao adicionar novas funções ao catálogo
  • o value de um default é sempre a própria expressão da função: value = "$(Totvs.Gateway.<key>)"

Customs

  • key deve ser único dentro do tenant
  • value pode ser literal, ou referenciar uma função $(Totvs.Gateway.*) ou $(input.*)
  • validado pelo HeaderValueValidator no 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-builder processa todos independente de posição
  • key deve 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-builder já 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 key e value livres
  • value deve 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-builder através do FieldResolver já existente
  • Chamar o gateway-ip-resolver apenas 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