Padrões de API Composition para Agregação de Dados entre Múltiplos Serviços
1. Fundamentos da API Composition em Arquiteturas Distribuídas
1.1 Definição e motivação
Em arquiteturas de microsserviços, cada serviço gerencia seu próprio domínio de dados. Quando uma aplicação cliente precisa exibir informações que combinam dados de múltiplos serviços — como detalhes de um pedido (serviço de pedidos) com dados do cliente (serviço de clientes) e status de entrega (serviço de logística) — surge a necessidade de um padrão de composição de APIs. A API composition é o mecanismo que coordena chamadas a múltiplos serviços e combina suas respostas em um único resultado coeso.
1.2 Problemas comuns
O principal desafio é o problema N+1 requests: para cada item de uma lista, o cliente precisa fazer uma requisição adicional para obter dados relacionados. Isso gera latência cumulativa e degradação da experiência do usuário. Outro problema é a inconsistência temporal — dados lidos de diferentes serviços em momentos distintos podem estar dessincronizados.
1.3 Trade-offs entre abordagens
A composição pode ocorrer em três camadas: no cliente (maior latência, acoplamento), no gateway (ponto único de orquestração) ou no backend (serviço dedicado de composição). Cada abordagem tem implicações diferentes em termos de acoplamento, performance e manutenibilidade.
2. Padrão API Gateway Composition
2.1 Implementação clássica
O gateway atua como orquestrador central, realizando chamadas paralelas aos serviços downstream. Exemplo de implementação:
GET /api/orders/composite/123
// Gateway faz chamadas paralelas:
// 1. Serviço de Pedidos: GET /orders/123
// 2. Serviço de Clientes: GET /customers/456
// 3. Serviço de Logística: GET /shipments/789
// Resposta agregada:
{
"orderId": 123,
"customer": { "name": "Maria Silva", "email": "maria@email.com" },
"items": [
{ "productId": 1, "name": "Notebook", "quantity": 1, "price": 4500.00 }
],
"shipment": { "status": "entregue", "trackingCode": "BR123456789" }
}
2.2 Estratégias de paralelismo
O fan-out controlado distribui as chamadas simultaneamente, mas com limites de concorrência para evitar sobrecarga nos serviços downstream:
// Configuração de fan-out com limite de 5 chamadas paralelas
// Timeout global: 2 segundos
// Timeout por serviço: 1 segundo
// Se um serviço falhar, retorna fallback parcial
2.3 Gerenciamento de erros parciais
// Exemplo de resposta com erro parcial:
{
"orderId": 123,
"customer": { "name": "Maria Silva", "email": "maria@email.com" },
"items": [ { "productId": 1, "name": "Notebook", "quantity": 1 } ],
"shipment": null,
"_errors": [
{ "service": "logistics", "message": "Serviço temporariamente indisponível" }
]
}
3. Padrão Backend-for-Frontend (BFF) para Composition
3.1 BFF como camada dedicada
O BFF é um serviço específico para cada tipo de cliente (web, mobile, IoT), que realiza a composição de dados de forma otimizada para aquele contexto. Isso evita que o gateway genérico precise lidar com todas as variações de resposta.
3.2 Customização de respostas
// BFF para cliente mobile retorna apenas campos essenciais:
{
"orderId": 123,
"customerName": "Maria Silva",
"total": 4500.00,
"status": "entregue"
}
// BFF para cliente web retorna dados completos:
{
"orderId": 123,
"customer": { "name": "Maria Silva", "email": "maria@email.com", "phone": "(11) 99999-9999" },
"items": [ { "productId": 1, "name": "Notebook", "specs": { "ram": "16GB", "storage": "512GB" } } ],
"shipment": { "status": "entregue", "trackingCode": "BR123456789", "history": [...] }
}
3.3 Cache de composição
// Cache de agregados pré-calculados (TTL: 5 minutos)
// Chave: composite:order:123
// Valor: objeto JSON completo do pedido agregado
// Invalidação: quando serviço de pedidos notifica alteração via evento
4. Padrão GraphQL como Motor de Composition
4.1 Resolvedores encadeados com DataLoader
GraphQL permite que o cliente especifique exatamente quais dados precisa, e o servidor utiliza resolvedores para buscar dados de múltiplas fontes. O DataLoader evita o problema N+1 ao agrupar chamadas:
// Query GraphQL:
query {
order(id: 123) {
id
customer { name email }
items { product { name price } }
}
}
// Resolvedor de customer usa DataLoader para agrupar chamadas:
// Em vez de N chamadas individuais, faz 1 chamada em lote
4.2 Batching e deduplicação
// DataLoader agrupa múltiplas requisições de customer:
// customersLoader.load(1), customersLoader.load(2), customersLoader.load(1)
// Resulta em: chamada única GET /customers?ids=1,2
// Deduplicação: customer 1 é carregado apenas uma vez
4.3 Limitações
Queries aninhadas profundamente podem gerar explosão de chamadas. É fundamental implementar limites de profundidade e análise de custo de queries.
5. Padrão de Composition com Event-Driven Architecture
5.1 Materialized views
Em vez de buscar dados sob demanda, serviços podem publicar eventos que alimentam agregados pré-construídos:
// Serviço de Pedidos publica evento: order.created
// Serviço de Clientes publica evento: customer.updated
// Serviço de Logística publica evento: shipment.status_changed
// Serviço de Composição escuta todos os eventos e mantém view materializada:
// Tabela: order_composite_view
// Atualizada via stream de eventos (Kafka, RabbitMQ)
5.2 CQRS e read models
Separação entre comandos (escrita) e consultas (leitura) permite otimizar o modelo de leitura para agregação:
// Read model otimizado para consulta de pedidos:
{
"orderId": 123,
"customerSummary": { "name": "Maria Silva", "tier": "gold" },
"itemCount": 3,
"totalValue": 7200.00,
"deliveryEstimate": "2024-01-15"
}
5.3 Consistência eventual vs. consistência forte
Para dashboards e relatórios, consistência eventual é aceitável. Para transações financeiras, consistência forte é necessária.
6. Estratégias de Otimização e Resiliência
6.1 Técnicas de compressão e projeção
// Resposta comprimida com campos projetados:
// - Remover campos nulos
// - Abreviar nomes de campos (ex: "cust" em vez de "customer")
// - Paginação de listas grandes
// - Compressão gzip em trânsito
6.2 Políticas de retry
// Política de retry com backoff exponencial:
// Tentativa 1: espera 100ms
// Tentativa 2: espera 200ms
// Tentativa 3: espera 400ms
// Máximo: 3 tentativas, timeout de 2s por tentativa
// Idempotência: usar idempotency-key para evitar duplicação
6.3 Monitoramento
// Métricas essenciais:
// - Latência p50, p95, p99 por endpoint de composição
// - Taxa de sucesso parcial vs. completo
// - Número de chamadas downstream por requisição
// - Cache hit ratio
7. Padrão de Composition com Async/Await e Streams
7.1 Agregação reativa
// Fluxo reativo usando streams assíncronos:
// 1. Cliente solicita dados do pedido
// 2. Serviço inicia composição e emite dados parciais conforme disponíveis
// 3. Primeiro: dados do pedido (rápido)
// 4. Segundo: dados do cliente (médio)
// 5. Terceiro: dados de logística (lento)
// 6. Cliente recebe atualizações incrementais
7.2 Combinação de protocolos
// Composição híbrida:
// - REST para dados de cliente (GET /customers/456)
// - gRPC para dados de estoque (stream bidirecional)
// - Mensageria para notificações de status (fila RabbitMQ)
7.3 Tratamento de timeouts parciais
// Se serviço de logística não responder em 500ms:
// - Retornar dados parciais com status "pendente"
// - Agendar retry assíncrono
// - Notificar cliente via webhook quando dados estiverem completos
8. Boas Práticas e Anti-Patterns
8.1 Evitar composition em cascata
// Anti-pattern: composição aninhada profunda
// Serviço A chama Serviço B que chama Serviço C
// Ideal: Serviço A faz chamadas paralelas para B e C
// Limite máximo de profundidade: 2 níveis
// Preferir fan-out paralelo a encadeamento sequencial
8.2 Versionamento de contratos
// Versionamento semântico dos endpoints de composição:
// /api/v1/composite/orders/123
// /api/v2/composite/orders/123
// Evolução gradual: novos campos são opcionais
// Deprecação: manter versão antiga por 6 meses
8.3 Documentação
Cada endpoint de composição deve documentar quais serviços são chamados, quais campos são opcionais, e exemplos de payloads agregados.
Referências
- API Composition Pattern - Microsoft Azure Architecture Center — Guia oficial da Microsoft sobre o padrão de composição de APIs em arquiteturas de microsserviços
- GraphQL DataLoader - Documentação Oficial — Explicação detalhada sobre batching e caching com DataLoader para evitar N+1 queries
- Backend for Frontend Pattern - Sam Newman (ThoughtWorks) — Artigo seminal do criador do padrão BFF, com exemplos práticos de implementação
- CQRS Pattern - Martin Fowler — Referência fundamental sobre Command Query Responsibility Segregation e sua aplicação em sistemas distribuídos
- Event-Driven Architecture - AWS Well-Architected Framework — Boas práticas para implementação de arquiteturas orientadas a eventos com materialized views
- Resilience Patterns: Circuit Breaker - Netflix Tech Blog — Artigo técnico sobre padrões de resiliência aplicados a composição de APIs
- gRPC vs REST for Microservices Communication - Google Cloud — Comparação técnica entre protocolos para comunicação entre serviços em composições