O padrão Strangler Fig: uma maneira segura de migrar aplicativos monolíticos
Na engenharia de software moderna, os aplicativos monolíticos legados são um desafio comum. Com o tempo, uma base de código bem-sucedida fica tão grande e interconectada que fazer alterações simples se torna arriscado, as implantações levam horas e o dimensionamento de recursos individuais é praticamente impossível.
Quando as equipes decidem modernizar seus sistemas migrando para microsserviços, elas enfrentam uma questão de alto risco: Como podemos reescrever o sistema sem interromper nossos negócios atuais?
Uma opção é reescrever o “Big Bang” – construir o novo sistema do zero, a portas fechadas, e mudar tudo em um único dia. No entanto, isso é extremamente arriscado e frequentemente leva ao fracasso.
Felizmente, existe uma alternativa mais segura e confiável: o Strangler Fig Pattern. Neste guia, exploraremos o que é o padrão Strangler Fig, por que ele funciona e como aplicá-lo passo a passo usando diagramas claros e código do mundo real.
A analogia do mundo real: a planta do figo estrangulador
O padrão leva o nome da figueira estranguladora, uma planta nativa das florestas tropicais.
Uma semente de figo estranguladora germina nos galhos superiores de uma árvore “hospedeira” existente. Em vez de crescer do zero, o figo cresce para baixo:
- Envia raízes pelo tronco da árvore hospedeira até atingirem o solo da floresta e se ancorarem no solo.
- Com o tempo, mais raízes crescem, envolvem o hospedeiro e se fundem.
- A figueira produz folhas que impedem que a luz chegue à árvore hospedeira.
- Eventualmente, a árvore hospedeira morre e apodrece, deixando uma figueira estranguladora oca firmemente em seu lugar.
Na arquitetura de software, o monólito legado é a árvore host, e os novos microsserviços são o estrangulador fig. Construímos os novos serviços em torno das bordas do monólito, desviando gradualmente o tráfego do sistema legado até que o monólito possa ser completamente desligado.
Por que as reescritas de “Big Bang” falham
Antes de mergulhar na mecânica do padrão Strangler Fig, vamos entender por que a alternativa – uma reescrita completa – é tão perigosa:
- Sem valor por meses (ou anos): os desenvolvedores passam muito tempo escrevendo código, mas nada dele entra em operação até que todo o projeto seja concluído.
- Aumento do escopo: Durante uma reescrita de dois anos, as necessidades de negócios mudam. O alvo se move e o novo sistema precisa suportar recursos que não existiam quando a reescrita começou.
- Comportamento implícito ausente: Monólitos contêm anos de correções de bugs não documentados e tratamentos de casos extremos. Uma reescrita do zero muitas vezes esquece esses detalhes.
- Alto risco de implantação: desligar um grande sistema legado e ligar um novo de uma só vez cria um enorme raio de explosão se algo der errado.
Como funciona o padrão Strangler Fig
A ideia central do Strangler Fig Pattern é migração incremental. Em vez de migrar todo o sistema, você migra um pequeno recurso ou “fatia” de cada vez.
O processo de migração é executado em cinco etapas principais:
1. Identifique um contexto limitado
Observe seu monólito e identifique um recurso de negócios único e independente que seja fácil de extrair. Bons candidatos incluem:
- Recursos que mudam frequentemente (para que a equipe se beneficie rapidamente de implantações independentes).
- Recursos simples e de baixo risco (como perguntas frequentes estáticas ou seção de preferências do usuário) para testar o pipeline de migração.
- Recursos com limites de banco de dados limpos e bem definidos.
2. Implementar o novo microsserviço
Desenvolva o recurso identificado como um microsserviço totalmente novo e moderno. Este serviço possui seu próprio banco de dados, seu próprio pipeline de implantação e é construído usando pilhas de tecnologia modernas. Crucialmente, o antigo recurso do monólito permanece ativo e inalterado por enquanto.
3. Apresente a camada de interceptação
Para tornar a migração transparente para seus usuários, você introduz uma Camada de interceptação (como um gateway de API ou proxy reverso) na frente do aplicativo. Todo o tráfego do cliente agora vai primeiro para este gateway.
Inicialmente, o gateway roteia 100% de todas as solicitações para o monolito legado.
4. Tráfego de transição incremental
Assim que o novo microsserviço estiver totalmente testado e pronto, você atualiza as regras de roteamento na camada de interceptação. Em vez de rotear solicitações do recurso migrado (por exemplo, /api/users) para o monólito, o gateway as redireciona para o novo microsserviço.
Todas as outras solicitações continuam indo para o monólito. Se o novo serviço falhar ou apresentar bugs, você poderá atualizar rapidamente o gateway para rotear o tráfego de volta ao monólito, garantindo interrupção mínima.
5. Desativação e repetição
Depois que o novo microsserviço for executado de forma estável por um período de tempo, você poderá excluir com segurança o código correspondente dentro do monólito legado.
Em seguida, você seleciona o próximo recurso e repete o processo. Com o tempo, o monólito encolhe até não ter mais tráfego e você pode desativar totalmente o servidor legado.
A camada de interceptação: exemplo de roteamento expresso
O coração do padrão Strangler Fig é a Camada de Intercepção. Ele permite redirecionar solicitações sem modificar os aplicativos clientes (aplicativos web, aplicativos móveis).
Aqui está um exemplo prático de Node.js usando um proxy de gateway baseado em Express. Ele roteia as solicitações dinamicamente: as solicitações recebidas vão para os novos serviços se corresponderem aos caminhos migrados; caso contrário, eles voltarão ao monólito legado.
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
const PORT = 8080;
// Configuration: Target server URLs
const LEGACY_MONOLITH_URL = 'http://legacy-monolith-server:3000';
const NEW_USER_SERVICE_URL = 'http://new-user-service:3001';
const NEW_PAYMENT_SERVICE_URL = 'http://new-payment-service:3002';
// Simple logging middleware to track traffic distribution
app.use((req, res, next) => {
console.log(`[ROUTE LOG] Incoming request: ${req.method} ${req.url}`);
next();
});
// 1. MIGRATED: User registration & profile requests route to the new microservice
app.use('/api/users', createProxyMiddleware({
target: NEW_USER_SERVICE_URL,
changeOrigin: true,
pathRewrite: {
'^/api/users': '/v1/users', // Translate path format if necessary
}
}));
// 2. MIGRATED: Payment transactions route to the new payment microservice
app.use('/api/payments', createProxyMiddleware({
target: NEW_PAYMENT_SERVICE_URL,
changeOrigin: true
}));
// 3. FALLBACK: All other legacy routes automatically default to the Monolith
app.use('/', createProxyMiddleware({
target: LEGACY_MONOLITH_URL,
changeOrigin: true
}));
app.listen(PORT, () => {
console.log(`Interception Gateway routing traffic successfully on port ${PORT}`);
});
Manipulando o banco de dados: a parte mais difícil
Embora o roteamento do tráfego da API seja relativamente fácil, o gerenciamento de dados é o aspecto mais desafiador da migração monolítica. Um monólito geralmente possui um banco de dados único e massivo onde as tabelas são altamente unidas.
Ao extrair um serviço, você também deve extrair seus dados. Existem duas abordagens comuns para lidar com isso:
- Escritas Duplas: A camada de interceptação ou o aplicativo grava dados no banco de dados legado e no novo banco de dados de serviço simultaneamente durante a fase de transição. Isso mantém os dois bancos de dados sincronizados.
- Change Data Capture (CDC): Uma ferramenta como o Debezium monitora o log de transações do banco de dados legado e transmite automaticamente as alterações para o novo banco de dados de microsserviços quase em tempo real.
Quando tiver certeza de que os bancos de dados estão totalmente sincronizados e que o novo banco de dados está correto, você alterna o tráfego de leitura para o novo serviço e desliga as tabelas herdadas.
Comparação: Big Bang Rewrite vs. Strangler Fig
| Recurso/métrica | Reescrita do Big Bang | Padrão de figo estrangulador |
|---|---|---|
| Nível de risco | Extremamente alto | Baixo e gerenciado |
| Círculo de Feedback | Muito Lento (apenas no final) | Rápido (testes de produção contínuos) |
| Estratégia de reversão | Difícil (requer restauração de backups) | Fácil (alterar regras de rota no gateway) |
| Impacto nos negócios | Disruptivo | Tempo de inatividade zero |
| Complexidade do sistema | Alto (durante a fase de construção) | Elevado (durante a fase de migração) |
| Tempo de implantação | Lançamento massivo de single | Atualizações pequenas e frequentes |
Conclusão
O Strangler Fig Pattern é o padrão da indústria para migração de monólitos para microsserviços. Ao substituir o código legado de forma incremental, em vez de tudo de uma vez, você elimina o risco de uma falha do “Big Bang”.
Ele mantém as implantações pequenas, fornece feedback instantâneo do tráfego de produção real e permite que sua equipe continue agregando valor aos negócios durante todo o ciclo de vida da migração.
Embora o gerenciamento da migração de bancos de dados e a execução de dois sistemas paralelos acrescentem complexidade operacional, a segurança, a previsibilidade e a estabilidade que oferece fazem dele a escolha preferida para migrações modernas para a nuvem.
Explore mais insights sobre desenvolvimento de software e engenharia de back-end no Blog Ghaznix →