Strangler Fig: migrando de monolito para microserviços
1. Introdução ao Padrão Strangler Fig
O padrão Strangler Fig foi inspirado no comportamento de uma figueira estranguladora, que cresce ao redor de uma árvore hospedeira, gradualmente a envolvendo e substituindo. Na arquitetura de software, o padrão propõe uma migração incremental de um sistema monolítico para microsserviços, substituindo funcionalidades antigas por novas implementações até que o monólito original seja completamente "estrangulado".
O problema central que este padrão resolve é o risco associado a migrações "big bang", onde todo o sistema é reescrito e substituído de uma só vez. Essas abordagens frequentemente resultam em atrasos, estouro de orçamento e falhas catastróficas. O Strangler Fig oferece uma alternativa segura: a migração contínua com redução de riscos e continuidade operacional.
2. Quando Aplicar o Padrão: Cenários e Decisões Arquiteturais
O padrão é mais adequado para monólitos legados com alta complexidade e acoplamento, onde uma reescrita total seria inviável. Cenários típicos incluem:
- Sistemas com lógica de negócio crítica que não podem sofrer interrupções
- Aplicações monolíticas que precisam de escalabilidade independente para funcionalidades específicas
- Equipes que desejam adotar deploy contínuo sem interromper operações existentes
Os trade-offs envolvem o custo de transformação versus os benefícios dos microsserviços. A migração incremental exige investimento contínuo em infraestrutura de roteamento, sincronização de dados e monitoramento, mas evita o risco de uma reescrita total.
3. Estrutura e Componentes do Strangler Fig
A estrutura básica do padrão envolve três componentes principais:
- Roteador/Proxy: Ponto de interceptação que decide se uma requisição vai para o módulo legado ou para o novo microsserviço
- Módulo legado: Sistema monolítico original que ainda executa funcionalidades não migradas
- Novos microsserviços: Implementações modernas que substituem partes do monólito
Exemplo de configuração de proxy reverso com NGINX:
# nginx.conf
upstream monolith {
server monolith-app:8080;
}
upstream new-service {
server new-microservice:3000;
}
server {
listen 80;
location /api/users {
# Roteamento para novo microsserviço
proxy_pass http://new-service;
}
location /api/orders {
# Roteamento para monólito legado
proxy_pass http://monolith;
}
}
4. Estratégias de Migração Incremental
Duas abordagens principais guiam a migração:
Abordagem por funcionalidade (feature-based): Identifica funcionalidades específicas do monólito e as substitui uma a uma. Exemplo:
# Roteamento por feature flag
if feature_flag.is_active("new-checkout"):
return new_checkout_service(request)
else:
return legacy_checkout_service(request)
Abordagem por domínio (bounded context): Utiliza princípios de Domain-Driven Design para identificar contextos delimitados dentro do monólito e migrá-los como microsserviços independentes.
Técnicas de estrangulamento incluem réplicas de dados temporárias e sincronização bidirecional entre o monólito e os novos serviços.
5. Desafios Técnicos e Padrões de Resiliência
O principal desafio é a consistência de dados entre sistemas. Durante a migração, dados precisam ser sincronizados entre o banco do monólito e os bancos dos microsserviços.
Padrão Saga para transações distribuídas:
# Exemplo de Saga Coreográfica
service OrderService:
on "OrderCreated":
PaymentService.processPayment(orderId)
service PaymentService:
on "PaymentProcessed":
InventoryService.reserveItems(orderId)
on "PaymentFailed":
OrderService.compensateOrder(orderId)
Circuit Breaker e Bulkhead isolam falhas durante a migração:
# Implementação de Circuit Breaker
if circuit_breaker.is_open("new-service"):
return legacy_service(request) # Fallback
else:
try:
return new_service(request)
except Exception:
circuit_breaker.record_failure()
return legacy_service(request)
6. Estratégias de Roteamento e Roteamento Inteligente
O roteamento inteligente permite controlar o tráfego entre o monólito e os microsserviços.
Feature flags com LaunchDarkly:
# Roteamento condicional
if launchdarkly.variation("use-new-payment", user):
return new_payment_service(request)
else:
return legacy_payment(request)
Canary releases para validar novos serviços:
# Roteamento por percentual de tráfego
if hash(user_id) % 100 < 5: # 5% dos usuários
return new_service(request)
else:
return legacy_service(request)
Dark launches executam ambos os serviços simultaneamente, comparando resultados sem impactar o usuário final.
7. Monitoramento e Validação Contínua
Métricas-chave para comparação entre monólito e microsserviço:
# Métricas comparativas
monolith_latency = 150ms
new_service_latency = 120ms
monolith_error_rate = 0.5%
new_service_error_rate = 0.3%
monolith_throughput = 1000 req/s
new_service_throughput = 1500 req/s
Rastreamento distribuído com OpenTelemetry:
# Span de rastreamento
span = tracer.start_span("process-order")
span.set_attribute("service", "new-checkout")
span.set_attribute("legacy_fallback", false)
Testes de equivalência funcional comparam saídas entre implementações legada e nova:
# Teste de equivalência
result_legacy = legacy_service(request)
result_new = new_service(request)
assert result_legacy == result_new
8. Quando Parar: Critérios de Sucesso e Encerramento
A migração é considerada completa quando:
- Todas as funcionalidades críticas foram migradas para microsserviços
- O tráfego para o monólito é zero ou mínimo
- Testes de equivalência funcional são bem-sucedidos para todos os cenários
Riscos de uma migração interminável incluem:
- Manutenção de duas bases de código simultaneamente
- Dívida técnica acumulada no monólito durante a migração
- Custo operacional duplicado
A decisão de manter partes do monólito é válida quando:
- Funcionalidades são raramente alteradas
- O custo de migração supera os benefícios
- O acoplamento com outras partes do sistema é mínimo
Uma estratégia híbrida pode ser a melhor opção para sistemas complexos.
Referências
- Martin Fowler - Strangler FigApplication — Artigo seminal que define o padrão Strangler Fig, com exemplos práticos e considerações arquiteturais.
- Microsoft Azure - Strangler Fig pattern — Documentação oficial da Microsoft sobre o padrão, incluindo diagramas e cenários de uso.
- Amazon Web Services - Migrating from a monolithic architecture — Guia prático da AWS para implementação do padrão Strangler Fig em migrações para microsserviços.
- NGINX - The Strangler Fig Pattern for Microservices Migration — Tutorial técnico da NGINX sobre como usar proxy reverso para implementar o padrão Strangler Fig.
- Red Hat - Strangler Fig pattern for application modernization — Artigo da Red Hat abordando o padrão no contexto de modernização de aplicações e arquitetura de microsserviços.
- OpenTelemetry - Distributed Tracing — Documentação oficial do OpenTelemetry para rastreamento distribuído, essencial para monitoramento durante migrações Strangler Fig.
- LaunchDarkly - Feature Flags for Migration — Guia da LaunchDarkly sobre uso de feature flags em estratégias de migração incremental.