API Gateway patterns: quando usar BFF, agregação e roteamento

1. O Papel do API Gateway na Arquitetura Moderna

1.1. Definição e responsabilidades: proxy reverso, segurança e roteamento

O API Gateway é um ponto único de entrada para requisições externas em arquiteturas de microsserviços. Ele atua como proxy reverso, centralizando segurança, roteamento e transformação de mensagens. Suas responsabilidades incluem:

  • Roteamento de requisições para serviços internos
  • Autenticação e autorização (JWT, OAuth2)
  • Rate limiting e throttling
  • Transformação de protocolos (HTTP para gRPC, por exemplo)
  • Logging e monitoramento centralizado

1.2. Diferença entre API Gateway e Service Mesh

Enquanto o API Gateway lida com tráfego de entrada (north-south), o Service Mesh gerencia comunicação entre serviços (east-west). O gateway é o porteiro; o mesh é o corredor interno. Ambos podem coexistir: o gateway roteia para o mesh, que gerencia retry, circuit breaker e observabilidade entre microsserviços.

1.3. Cenários onde o gateway é indispensável vs. overengineering

Indispensável Overengineering
Múltiplos microsserviços com diferentes protocolos Monólito com 2 endpoints
Necessidade de autenticação centralizada Aplicação interna sem exposição pública
Clientes heterogêneos (web, mobile, IoT) API única consumida por um só frontend

2. Roteamento Simples: O Padrão Gateway Tradicional

2.1. Funcionamento: mapeamento de paths para microsserviços

O roteamento simples mapeia URLs para serviços internos sem transformar dados:

GET /api/users/* → user-service:3001
GET /api/orders/* → order-service:3002
POST /api/payments/* → payment-service:3003

2.2. Vantagens e limitações

Vantagens: Simplicidade, baixa latência, fácil manutenção.
Limitações: Sem composição de dados, cliente precisa fazer múltiplas chamadas.

2.3. Exemplo prático: configuração de roteamento com NGINX/Kong

Configuração NGINX para roteamento simples:

server {
    listen 80;

    location /api/users {
        proxy_pass http://user-service:3001;
    }

    location /api/orders {
        proxy_pass http://order-service:3002;
    }

    location /api/products {
        proxy_pass http://product-service:3003;
    }
}

3. Padrão de Agregação: Unificando Respostas de Múltiplos Serviços

3.1. Como funciona a agregação (composição de dados no gateway)

O gateway agrega chamadas paralelas a múltiplos serviços e combina as respostas em um único payload:

GET /api/dashboard/{userId}

Gateway faz 3 chamadas paralelas:
→ user-service: GET /users/{userId}
→ order-service: GET /orders?userId={userId}
→ inventory-service: GET /inventory/status

Resposta agregada:
{
  "user": { "name": "João", "email": "joao@email.com" },
  "recent_orders": [ ... ],
  "inventory_summary": { "total_items": 150 }
}

3.2. Trade-offs: redução de chamadas client-side vs. acoplamento

Prós: Reduz chamadas do cliente, otimiza payload, lógica de composição centralizada.
Contras: Gateway mais complexo, acoplamento entre gateway e serviços, ponto único de falha.

3.3. Caso de uso: dashboard que consome dados de usuários, pedidos e estoque

Ideal para dashboards que precisam exibir informações de múltiplos domínios em uma única tela, reduzindo latência de rede para o cliente.

4. Backend for Frontend (BFF): Gateways Especializados por Cliente

4.1. Conceito: um gateway específico para cada tipo de frontend

Cada cliente (web, mobile, IoT) tem seu próprio gateway que expõe exatamente os dados e formatos que precisa:

/web-gateway → serviços otimizados para navegador
/mobile-gateway → payloads menores, dados resumidos
/iot-gateway → protocolo MQTT, dados binários

4.2. Benefícios: payloads otimizados, lógica de apresentação isolada

  • Web: dados completos, suporte a cache, paginação
  • Mobile: payloads mínimos, campos essenciais, compressão
  • IoT: formato binário, baixa latência, tolerância a falhas

4.3. Quando usar BFF: aplicações com requisitos de dados muito distintos entre clientes

Use BFF quando:
- Mobile precisa de dados diferentes do web (ex: sem imagens grandes)
- IoT envia dados em formato binário enquanto web usa JSON
- Cada cliente tem requisitos de performance distintos

5. Comparação Direta: Roteamento vs. Agregação vs. BFF

5.1. Tabela de critérios

Critério Roteamento Agregação BFF
Complexidade Baixa Média Alta
Performance Alta Média Alta (por cliente)
Manutenibilidade Alta Média Média (vários gateways)
Reuso de lógica Nenhum Centralizado Por cliente
Acoplamento Baixo Médio Baixo (separado)

5.2. Árvore de decisão

Único tipo de cliente?
├── Sim → Precisa agregar dados?
│   ├── Sim → Agregação
│   └── Não → Roteamento simples
└── Não → BFF

5.3. Exemplo híbrido: combinando roteamento com BFF

Arquitetura real que combina padrões:

Gateway principal (roteamento)
├── /web/* → Web BFF (agregação para navegador)
├── /mobile/* → Mobile BFF (payloads leves)
└── /api/public/* → Roteamento direto (API pública)

6. Considerações de Performance e Escalabilidade

6.1. Latência adicional: gerenciar overhead do gateway

Cada camada adiciona latência. Mitigações:
- Conexões persistentes (keep-alive)
- Processamento assíncrono (Promise.all para agregação)
- Cache de respostas frequentes

6.2. Cache no gateway: estratégias para reduzir carga nos serviços

Cache por endpoint:
/users/:id → TTL 60s (dados pouco voláteis)
/products/search → TTL 30s (resultados de busca)
/orders/:id → Sem cache (dados críticos)

6.3. Escalabilidade horizontal: gateways stateless e rate limiting

Gateways devem ser stateless para escalar horizontalmente. Rate limiting por IP, chave de API ou usuário:

Rate limit: 100 req/min por usuário
Rate limit: 1000 req/min global

7. Segurança e Observabilidade no API Gateway

7.1. Autenticação e autorização centralizadas (JWT, OAuth2)

Requisição → Gateway valida JWT → Extrai claims → 
Encaminha user_id e roles para serviços internos via headers

7.2. Logging e tracing distribuído (OpenTelemetry, Zipkin)

Headers de tracing:
x-request-id: uuid
x-trace-id: uuid
x-span-id: uuid

7.3. Políticas de throttling e circuit breaker

Throttling: 50 req/s por cliente
Circuit breaker: 5 falhas em 10s → abre circuito por 30s

8. Implementação Prática: Três Padrões com Exemplos

8.1. Roteamento puro: configurando um gateway com Express Gateway

// Express Gateway config
const gateway = require('express-gateway');

gateway.config = {
  apiEndpoints: {
    users: { host: 'user-service', paths: '/users/*' },
    orders: { host: 'order-service', paths: '/orders/*' }
  },
  policies: ['proxy', 'rate-limiter'],
  pipelines: {
    default: {
      apiEndpoints: ['users', 'orders'],
      policies: {
        'rate-limiter': { action: { max: 100 } },
        'proxy': []
      }
    }
  }
};

8.2. Agregação: implementando um endpoint composto com Apollo Federation

// Gateway Apollo Federation
const { ApolloGateway } = require('@apollo/gateway');

const gateway = new ApolloGateway({
  serviceList: [
    { name: 'users', url: 'http://user-service:4001' },
    { name: 'orders', url: 'http://order-service:4002' },
    { name: 'inventory', url: 'http://inventory-service:4003' }
  ]
});

// Query automática que agrega dados
query DashboardData($userId: ID!) {
  user(id: $userId) {
    name
    orders { total }
    inventory { items }
  }
}

8.3. BFF: criando gateways separados para web e mobile com Node.js

// Web BFF - dados completos
const webGateway = express();
webGateway.get('/dashboard/:userId', async (req, res) => {
  const user = await getUser(req.params.userId);
  const orders = await getOrders(req.params.userId);
  res.json({ user, orders, recommendations: true });
});

// Mobile BFF - dados mínimos
const mobileGateway = express();
mobileGateway.get('/dashboard/:userId', async (req, res) => {
  const user = await getUser(req.params.userId);
  const orders = await getOrders(req.params.userId);
  res.json({ 
    name: user.name,
    order_count: orders.length 
  });
});

Referências