---
id: distributed-patterns-cheatsheet
domain: architecture
agents: [architect]
when: "ao escolher protocolos, comunicação e padrões de resiliência num sistema distribuído"
---

# Distributed Patterns — cheatsheet de decisão "use X quando Y"

## O problema

A maioria das arquiteturas distribuídas erra não por falta de conhecimento, mas por **default não justificado**: escolhe-se REST porque é o que se conhece, retry porque "é seguro", blue-green porque soa robusto — sem amarrar a escolha ao critério que a justifica. O resultado é previsível: gRPC interno que ninguém consegue debugar no browser, retries que amplificam um incidente em vez de absorvê-lo, rate limiter que deixa passar o dobro do limite na virada da janela, deploy que custa 2x sem precisar.

Este pack é uma **régua de decisão**. Cada item tem o **critério de escolha** (o "quando Y") e o **trade-off** que você está aceitando. Se você não consegue nomear o Y, não escolha o X — o default está te traindo.

## Os princípios / o conhecimento

### 1. Estilo de API — REST vs GraphQL vs gRPC vs WebSocket

A confusão comum é tratá-los como concorrentes. **São complementos.** O mapa mental sólido: REST para API pública, gRPC para serviço interno, GraphQL para necessidade de dados orientada ao cliente, WebSocket para tempo real bidirecional.

| Estilo | Transporte / formato | Use quando | Trade-off que você aceita |
|---|---|---|---|
| **REST** | HTTP/1.1 + JSON | API pública, integração externa, recursos CRUD, quando cache de infra (CDN/proxy) importa | Over/under-fetching; múltiplos round-trips para dados relacionados |
| **GraphQL** | HTTP + schema/query | Cliente decide o shape dos dados; agrega N fontes num gateway; payloads onde se pega só o necessário | Cache HTTP fraco (POST único); complexidade de servidor; risco de query cara N+1 |
| **gRPC** | HTTP/2 + Protobuf (binário, IDL) | Comunicação serviço-a-serviço interna, baixa latência, contratos fortes, streaming | **Não roda no browser** (XHR não fala HTTP/2 puro); binário não é human-readable; sem cache HTTP nativo |
| **WebSocket** | TCP full-duplex, conexão longa | Tempo real bidirecional (chat, presença, dados ao vivo) entre muitos clientes e o servidor | Conexão persistente consome recurso do servidor; escala horizontal exige sticky session / estado distribuído |

**Números reais para calibrar:**
- gRPC é tipicamente **5 a 10x mais rápido que REST** em benchmarks — vem do HTTP/2 (multiplexing de várias requests numa conexão) + Protobuf binário.
- WebSocket ganha em **latência para mensagens pequenas e frequentes** porque elimina o handshake repetido — exatamente onde REST sangra.

**Tells de escolha errada:**
- gRPC exposto direto ao browser → precisa de gRPC-Web/proxy; sinal de que REST ou GraphQL era o certo na borda.
- WebSocket para request-response pontual → você pagou conexão persistente por nada.
- GraphQL numa API com 3 endpoints estáveis → complexidade sem retorno; REST resolvia.

### 2. API Gateway — a borda única

O gateway é o ponto de entrada que faz o que cada serviço não deve repetir: **autenticação/autorização, rate limiting, roteamento, agregação de respostas, terminação TLS, observabilidade**. Use quando há múltiplos serviços atrás de uma fachada e você quer cross-cutting concerns num lugar só.

| Responsabilidade | Por que no gateway, não no serviço |
|---|---|
| AuthN / AuthZ | Não duplicar verificação de token em N serviços |
| Rate limiting | Proteger o backend inteiro de um cliente abusivo |
| Roteamento / versionamento | Desacoplar URL pública da topologia interna |
| Agregação | Compor resposta de vários serviços (BFF) numa chamada |
| TLS termination / observabilidade | Ponto único de métricas, logs, tracing |

**Trade-off:** o gateway é um **single point of failure** e ponto de latência — exige HA e cuidado para não virar monolito disfarçado. Não coloque lógica de negócio nele.

### 3. Síncrono vs Assíncrono

| Critério | Síncrono (request-response) | Assíncrono (mensagem/evento) |
|---|---|---|
| Acoplamento temporal | Forte — chamador espera | Fraco — fire-and-forget |
| Use quando | Precisa da resposta agora para continuar (consulta, validação) | Pode processar depois; desacoplar produtor e consumidor; absorver picos |
| Falha do downstream | Propaga imediatamente ao chamador | Absorvida pela fila; reprocessa depois |
| Trade-off | Cascata de latência e de falha entre serviços | Complexidade: eventual consistency, ordenação, idempotência obrigatória |

Regra prática: **toda cadeia síncrona longa é uma cascata de falha esperando acontecer.** Se a operação não precisa do resultado em linha, torne-a assíncrona.

### 4. Message Queue vs Event Streaming

Ambos são assíncronos, mas resolvem coisas diferentes. Confundir os dois é um erro clássico.

| Dimensão | Message Queue (ex.: RabbitMQ, SQS) | Event Streaming (ex.: Kafka) |
|---|---|---|
| Modelo | Fila: mensagem **consumida e removida** | Log append-only: evento **retido e re-lido** |
| Consumidores | Tipicamente 1 consumidor por mensagem (work queue) | N consumidores independentes, cada um com seu offset |
| Replay | Não (mensagem some após ack) | Sim (re-processa do offset, retém por tempo/tamanho) |
| Ordem | Por fila | Por partição |
| Use quando | Distribuir tarefas/jobs, desacoplar work, balancear carga | Pipeline de eventos, event sourcing, múltiplos consumidores, auditoria/replay, alto throughput |
| Trade-off | Sem histórico; difícil ter múltiplas views do mesmo evento | Mais operacional (partições, offsets, retenção); ordem só dentro da partição |

**Por que Kafka é rápido (para citar):** escrita sequencial em disco (append-only), zero-copy no envio ao consumidor, e batching — evita o custo de I/O aleatório.

### 5. Idempotência

Idempotência é a **pré-condição de qualquer retry seguro**. Operação idempotente = executá-la N vezes tem o mesmo efeito de executá-la uma vez. Sem isso, retry vira cobrança duplicada.

**Top casos onde aplicar idempotência:**
1. **Pagamentos / cobrança** — retry não pode cobrar duas vezes.
2. **Criação de recurso** (POST que cria pedido) — request duplicado não cria dois pedidos.
3. **Consumo de mensagem** (at-least-once delivery) — a mesma mensagem pode chegar 2x.
4. **Webhooks** — provedores reenviam em caso de timeout.
5. **Retries automáticos** entre serviços.
6. **Operações disparadas por usuário** (double-click no "Comprar").

**Como implementar:** **idempotency key** — o cliente envia um ID único (header `Idempotency-Key`); o servidor guarda o resultado da primeira execução e, em requests repetidos com a mesma key, retorna o resultado cacheado em vez de re-executar.

### 6. Retries — estratégias

Retry resolve **falha transitória** (timeout momentâneo, blip de rede). Não resolve serviço caído — para isso é circuit breaker.

| Estratégia | Comportamento | Risco |
|---|---|---|
| **Retry imediato** | Tenta de novo na hora | Pode martelar serviço já estressado |
| **Intervalo fixo** | Espera X ms entre tentativas | Retries sincronizados de N clientes = thundering herd |
| **Exponential backoff** | Atraso cresce a cada tentativa (1s, 2s, 4s, 8s…) | Reduz pressão, mas ainda sincroniza se todos começam juntos |
| **Backoff + jitter** | Backoff exponencial **com aleatoriedade** no intervalo | **Recomendado** — espalha os retries, evita retry storm / thundering herd |

**Regras invioláveis do retry:**
- **Limite o número** de tentativas (bounded) — retry infinito amplifica incidente.
- **Só retentar operação idempotente** — senão duplica efeito colateral.
- **Instrumente** — sem métrica, retries escondem o incidente em vez de absorvê-lo.
- **Só para transitório** — se a falha é persistente, retry só piora.

### 7. Resiliência — Circuit Breaker

O circuit breaker protege contra **falha não-transitória** (serviço down): para de chamar o downstream para não desperdiçar recurso e dar fôlego pra ele se recuperar. Três estados:

| Estado | Comportamento | Transição |
|---|---|---|
| **CLOSED** | Operação normal; todas as requests passam | Falhas acima do threshold → OPEN |
| **OPEN** | Bloqueia tudo; **falha imediata** sem chamada de rede | Após timeout configurado (tipicamente 30s a poucos minutos) → HALF-OPEN |
| **HALF-OPEN** | Deixa passar **1 ou poucas** requests de teste | Sucesso → CLOSED; falha → volta a OPEN e reinicia o timer |

**Circuit breaker vs Retry — quando cada um:**

| | Retry | Circuit Breaker |
|---|---|---|
| Trata | Falha **transitória** (blip momentâneo) | Falha **persistente** (serviço provavelmente down) |
| Efeito | Tenta de novo | Para de tentar para não piorar |
| Juntos | Retry **dentro** do CLOSED; o breaker corta o retry quando vira OPEN | Complementares, não substitutos |

Combine com **bulkhead** (isolar thread pools para que um downstream lento não consuma todas as threads) e **timeout** (toda chamada remota tem prazo).

### 8. Resiliência — Rate Limiting (os 5 algoritmos)

Escolher o algoritmo errado custa caro: ou você deixa passar burst que derruba o backend (boundary exploit), ou gasta memória demais para precisão que não precisa.

| Algoritmo | Como funciona | Use quando | Trade-off |
|---|---|---|---|
| **Token Bucket** | Bucket enche tokens a taxa fixa; cada request consome 1; bucket = capacidade de burst | **Default para API pública** — modela burst separado da taxa sustentada; permite picos controlados reais | Precisa afinar tamanho do bucket vs taxa de refill |
| **Leaky Bucket** | Fila drena a taxa **constante**; request entra na fila, rejeitado se cheia | Saída suave e constante, independente do burst de entrada (proteger downstream que odeia picos) | Enfileira → adiciona latência; descarta na fila cheia |
| **Fixed Window Counter** | Conta requests por janela fixa (ex.: por minuto) | Simplicidade máxima; throttle de login, limites básicos, serviço interno onde aproximação serve | **Boundary exploit**: na virada da janela pode passar até 2x o limite |
| **Sliding Window Log** | Guarda timestamp **exato** de cada request recente | Precisão máxima e auditoria — pagamento, autenticação, endpoint que exige contagem exata | Maior custo de memória e operação (guarda cada timestamp) |
| **Sliding Window Counter** | Aproxima a janela deslizante ponderando janela atual + anterior | **Melhor compromisso em escala distribuída** — reduz o boundary burst com custo baixo, alto throughput | Aproximação (não tão exato quanto o log), mas bom o suficiente |

**Regra de bolso:** API pública → **token bucket**. Precisa de exatidão/auditoria → **sliding window log**. Escala distribuída com eficiência → **sliding window counter**. Saída precisa ser perfeitamente suave → **leaky bucket**. Só simplicidade e tolera burst de borda → **fixed window**.

### 9. Estratégias de Deploy

| Estratégia | Como | Downtime | Rollback | Risco | Custo infra | Complexidade | Use quando |
|---|---|---|---|---|---|---|---|
| **Rolling** | Substitui instâncias aos poucos | Não | **Lento** | Médio | 1x–1.25x | Baixa | Bug fixes de rotina; default barato |
| **Blue-Green** | Dois ambientes idênticos; troca **todo** o tráfego de uma vez | Não | **Instantâneo** | Baixo | **2x** | Média | Patch crítico de segurança; release que exige rollback imediato |
| **Canary** | Roteia % pequeno (ex.: 25%) pro novo, observa, então expande | Não | **Rápido** | Baixo | 1x–1.1x | **Alta** | Release de feature grande; quer validar com tráfego real antes do full rollout |
| **A/B Testing** | Variantes para medir comportamento (experimento, não mitigação) | Não | Rápido | **Maior** (sem rollback por erro/latência automático) | 1x–1.1x | Média | Experimento de produto; ambas versões já estáveis |
| **Shadow** | Espelha tráfego real pro novo **sem retornar resposta ao usuário** | Não | N/A | Nenhum (usuário não afetado) | 2x | Alta | Validar código novo com workload real sem impacto no usuário |

**Feature flags são ortogonais:** desacoplam **deploy de release**. Você faz deploy do código com qualquer estratégia, mas mantém a feature desligada e liga via config — gradualmente, sem novo deploy.

**Critério de escolha resumido:** bug fix rotineiro → rolling; feature grande → canary; patch crítico que precisa de reversão instantânea → blue-green; quer medir comportamento → A/B; quer testar sob carga real sem risco → shadow.

## Checklist

Antes de fechar a arquitetura, responda — cada "não" é um default não justificado a revisar:

- [ ] Para cada API, eu sei nomear **por que** REST/GraphQL/gRPC/WebSocket e não o outro?
- [ ] Tem gRPC que precisa ser consumido pelo browser? (vai precisar de proxy/gRPC-Web)
- [ ] Cross-cutting concerns (auth, rate limit, TLS) estão no **gateway**, não duplicados nos serviços?
- [ ] As cadeias síncronas longas foram revisadas — o que pode ser assíncrono virou assíncrono?
- [ ] A escolha fila vs streaming bate com a necessidade de **replay / múltiplos consumidores**?
- [ ] Toda operação retentável é **idempotente** (idempotency key onde há efeito colateral)?
- [ ] Os retries têm **limite**, usam **backoff + jitter**, e são **só para falha transitória**?
- [ ] Chamadas a downstream têm **timeout + circuit breaker** (e bulkhead onde compartilham pool)?
- [ ] O algoritmo de rate limiting foi escolhido pelo critério (token bucket default; log p/ exatidão; counter p/ escala)?
- [ ] A estratégia de deploy bate com o **custo de rollback** aceitável (instantâneo → blue-green; gradual → canary)?
- [ ] Feature flags separam **deploy de release** onde faz sentido?

## Tabela de decisão "use X quando Y"

| Decisão (X) | Escolha quando (Y) | Trade-off aceito |
|---|---|---|
| **REST** | API pública/externa, CRUD, cache de CDN importa | Over/under-fetching, round-trips |
| **GraphQL** | Cliente dita o shape; agrega N fontes | Cache HTTP fraco, risco N+1 |
| **gRPC** | Serviço↔serviço interno, baixa latência, contrato forte | Sem browser nativo, binário, sem cache HTTP |
| **WebSocket** | Tempo real bidirecional, muitas conexões vivas | Recurso por conexão, sticky sessions na escala |
| **API Gateway** | N serviços atrás de uma borda com cross-cutting concerns | SPOF + latência; exige HA |
| **Assíncrono** | Não precisa da resposta em linha; absorver picos | Eventual consistency, idempotência obrigatória |
| **Message Queue** | Distribuir jobs, 1 consumidor por mensagem | Sem replay/histórico |
| **Event Streaming** | Múltiplos consumidores, replay, auditoria, alto throughput | Ops de partição/offset/retenção |
| **Idempotency key** | Qualquer operação com efeito colateral que pode ser retentada | Estado extra para guardar resultados |
| **Backoff + jitter** | Retry de falha transitória sob concorrência | Latência adicional na recuperação |
| **Circuit Breaker** | Downstream provavelmente down (falha persistente) | Falha rápida temporária enquanto OPEN |
| **Token Bucket** | Rate limit de API pública com burst real | Afinar bucket vs refill |
| **Sliding Window Log** | Rate limit com exatidão/auditoria (pagamento, auth) | Custo de memória por timestamp |
| **Sliding Window Counter** | Rate limit em escala distribuída eficiente | Aproximação (não exato) |
| **Leaky Bucket** | Saída precisa ser constante/suave pro downstream | Latência de fila; descarte na fila cheia |
| **Fixed Window** | Throttle simples, tolera burst de borda | Boundary exploit (até 2x na virada) |
| **Rolling deploy** | Bug fix de rotina, custo baixo | Rollback lento |
| **Blue-Green** | Patch crítico, rollback instantâneo necessário | Custo de infra 2x |
| **Canary** | Feature grande, validar com tráfego real | Alta complexidade de pipeline |
| **Shadow** | Testar sob carga real sem risco ao usuário | Custo 2x, sem resposta ao usuário |
| **Feature flags** | Separar deploy de release; rollout gradual por config | Gestão de flags / débito se não limpar |

---

> **Fonte de referência:** ByteByteGo — *system-design-101* (github.com/ByteByteGoHq/system-design-101), seções "SOAP vs REST vs GraphQL vs RPC", "What is gRPC?", "API Gateway 101", "Types of Message Queues", "Kafka 101 / Why is Kafka Fast?", "Top 6 Cases to Apply Idempotency", "Retry Strategies for System Failures", "Resiliency Patterns", "Top 5 Most-Used Deployment Strategies". Números e critérios consolidados com fontes públicas de engenharia (rate limiting, circuit breaker, deployment trade-offs).
