Boas práticas de gestão de dependências externas com fallback
1. Fundamentos da gestão de dependências externas
Dependências externas são serviços, APIs, bancos de dados ou bibliotecas que um sistema consome para executar suas funcionalidades. Em sistemas distribuídos modernos, é raro encontrar uma aplicação que não dependa de pelo menos um recurso externo — seja um gateway de pagamento, um serviço de autenticação ou uma API de terceiros.
Os riscos associados a essas dependências são variados: indisponibilidade temporária, latência elevada, mudanças inesperadas de contrato de API, versões incompatíveis e throttling. Sem uma estratégia de fallback, qualquer falha em uma dependência externa pode se propagar para o usuário final, resultando em erros 500, timeouts ou degradação total do sistema.
O fallback é uma estratégia de resiliência que define um plano B quando o recurso primário falha. Ele garante continuidade operacional, mesmo que com funcionalidades reduzidas, e é essencial para manter a experiência do usuário em cenários adversos.
2. Padrões de fallback: circuit breaker e retry com backoff
Dois padrões fundamentais para gestão de dependências externas são o Circuit Breaker e o Retry com backoff.
O Circuit Breaker monitora chamadas a um serviço externo e mantém três estados:
- Fechado: operação normal, chamadas são enviadas diretamente.
- Aberto: após um número configurável de falhas consecutivas, o circuito abre e todas as chamadas falham imediatamente sem tentar o serviço.
- Semi-aberto: após um tempo de espera, algumas requisições são permitidas para testar se o serviço se recuperou.
Exemplo conceitual de estados do Circuit Breaker:
Estado: FECHADO
-> 5 falhas consecutivas
-> Transição para ABERTO
-> Espera 30 segundos
-> Transição para SEMI-ABERTO
-> 3 requisições de teste
Se sucesso: volta para FECHADO
Se falha: volta para ABERTO
O padrão Retry com exponential backoff tenta novamente a operação após um intervalo crescente entre tentativas. O jitter adiciona aleatoriedade ao intervalo para evitar que múltiplos clientes ataquem o serviço ao mesmo tempo:
Tentativa 1: espera 1 segundo
Tentativa 2: espera 2 segundos
Tentativa 3: espera 4 segundos
Tentativa 4: espera 8 segundos + jitter aleatório
A combinação ideal é: primeiro aplicar retry com backoff para erros transitórios (timeouts, 503), e após esgotar as tentativas, ativar o Circuit Breaker para evitar sobrecarga. O fallback deve ser acionado quando o circuito está aberto ou após todas as tentativas falharem.
3. Estratégias de fallback: cache, dados estáticos e degradação funcional
Existem três estratégias principais de fallback para dependências externas:
Fallback via cache: armazena respostas bem-sucedidas anteriores com um TTL (time-to-live) adequado. Se o serviço externo falhar, o sistema retorna o dado em cache, mesmo que ligeiramente desatualizado.
Tentativa: consultar API de preços
Sucesso -> armazenar em cache por 5 minutos
Falha -> retornar cache (máximo 30 minutos de idade)
Fallback com dados estáticos: para funcionalidades não críticas, use valores padrão ou dados embutidos no código. Exemplo: se o serviço de cotação de moedas falha, usar uma taxa fixa definida em configuração.
Degradação graciosa: desabilite funcionalidades secundárias mantendo o core. Por exemplo, se o sistema de recomendações falha, o usuário ainda pode comprar produtos, apenas sem sugestões personalizadas.
4. Implementação prática de fallback com timeouts e health checks
Timeouts são cruciais para evitar que uma dependência lenta trave todo o sistema. Configure timeouts por chamada e por componente:
Configurações de timeout:
API de pagamentos: 5 segundos
Serviço de notificações: 3 segundos
Consulta de estoque: 2 segundos
Timeout total da operação: 10 segundos
Health checks periódicos monitoram a disponibilidade do serviço externo:
Health Check a cada 30 segundos:
GET /health do serviço externo
Resposta 200 -> serviço saudável
Resposta 5xx ou timeout -> serviço indisponível
Atualizar status interno: "disponível" ou "falha"
Quando o health check indica recuperação, o sistema pode reativar gradualmente as chamadas diretas, primeiro com tráfego reduzido (canary) e depois com carga total.
5. Tratamento de erros e logging estruturado em cenários de fallback
É essencial diferenciar erros transitórios (que podem ser resolvidos com retry) de erros permanentes (que exigem fallback imediato):
Erro transitório: timeout, 503 Service Unavailable, 429 Too Many Requests
Ação: retry com backoff
Erro permanente: 400 Bad Request, 404 Not Found, 500 Internal Server Error
Ação: fallback imediato, sem retry
Logs estruturados devem conter contexto suficiente para debug:
Log de fallback ativado:
{
"request_id": "abc-123",
"servico": "api-pagamentos",
"erro": "timeout após 5s",
"tentativas": 3,
"fallback_ativado": "cache",
"idade_cache": "2 minutos",
"usuario_id": "user-456",
"timestamp": "2025-01-15T10:30:00Z"
}
Métricas importantes: taxa de falhas por serviço, número de ativações de fallback, latência média e tempo de recuperação. Alertas devem ser configurados para quando a taxa de fallback ultrapassar 10% em um período de 5 minutos.
6. Testes e validação de resiliência com fallback
Testes de integração devem simular falhas de dependências externas:
Cenário de teste: API de frete indisponível
1. Mockar API de frete para retornar 503
2. Chamar serviço de checkout
3. Verificar se fallback para cache é ativado
4. Verificar se log de fallback é gerado
5. Verificar se usuário vê preço base (não personalizado)
Chaos engineering permite testar resiliência em produção ou staging controlada:
Experimento de caos:
1. Injetar latência de 10 segundos em 30% das chamadas à API de estoque
2. Observar comportamento do Circuit Breaker
3. Verificar tempo de resposta do sistema com fallback
4. Medir impacto na experiência do usuário
A validação deve garantir que o sistema degrada graciosamente e não quebra completamente em cenários extremos.
7. Exemplo completo: dependência externa com fallback em camadas
Abaixo, um fluxo típico de fallback em três camadas para uma API de preços:
Função obterPrecoProduto(produtoId):
1. TENTATIVA DIRETA
Chamar API de preços externa
Timeout: 3 segundos
Se sucesso:
Atualizar cache local com TTL 5 minutos
Retornar preço obtido
Se falha (timeout ou erro 5xx):
Ir para passo 2
2. RETRY COM BACKOFF
Para tentativa = 1 até 3:
Aguardar (2^tentativa) segundos + jitter
Tentar novamente chamada à API
Se sucesso:
Atualizar cache
Retornar preço obtido
Se todas as tentativas falharem:
Ir para passo 3
3. FALLBACK VIA CACHE
Verificar cache local
Se cache existe e idade < 30 minutos:
Retornar preço do cache
Registrar log: "fallback_cache"
Se cache não existe ou expirado:
Ir para passo 4
4. FALLBACK ESTÁTICO
Retornar preço padrão de configuração
Registrar log: "fallback_estatico"
Ativar alerta de indisponibilidade
Comparação de cenários:
Sem fallback:
API falha -> usuário vê erro 500 -> abandona compra
Com fallback:
API falha -> fallback cache -> usuário vê preço (possivelmente desatualizado)
Cache expirado -> fallback estático -> usuário vê preço padrão
Experiência mantida, mesmo com dados imperfeitos
A implementação de fallback em camadas garante que o sistema nunca retorne um erro para o usuário final em funcionalidades não críticas, mantendo a continuidade do negócio mesmo sob falhas de dependências externas.
Referências
- Microsoft - Padrão Circuit Breaker — Documentação oficial da Microsoft sobre o padrão Circuit Breaker para resiliência em sistemas distribuídos.
- AWS - Padrão Retry com Exponential Backoff — Artigo técnico da AWS explicando retry com backoff exponencial e jitter.
- Netflix - Chaos Engineering — Blog técnico da Netflix sobre práticas de chaos engineering para testar resiliência.
- Resilience4j - Documentação Oficial — Biblioteca Java para implementação de Circuit Breaker, Retry e outras estratégias de resiliência.
- Google SRE - Handling Overload — Capítulo do livro Google SRE sobre tratamento de sobrecarga e fallback em sistemas de larga escala.
- Martin Fowler - CircuitBreaker — Artigo clássico de Martin Fowler sobre o padrão Circuit Breaker e sua aplicação.
- Hystrix - Documentação (Arquivo) — Repositório oficial do Hystrix da Netflix, biblioteca que popularizou o padrão Circuit Breaker.