Pular para o conteúdo

Padrão Strangler Fig: migrando um monólito passo a passo, com diagrama

Como substituir um monólito com o padrão strangler fig: uma fachada de roteamento, quatro fases, o que extrair primeiro, dados sem dupla escrita e prova de paridade.

· Fundador e desenvolvedor

Publicado em

Clientes chamam um API gateway que faz de fachada. Ele envia /catalog/* (100 por cento) a um serviço de catálogo com banco próprio, 25 por cento de GET /orders/* a um serviço de pedidos e o resto ao monólito, cujo banco alimenta o de pedidos por um caminho CDC tracejado.

Seu monólito tem dez anos, cada release exige um longo ciclo de regressão e alguém propõe uma reescrita: construir o sistema novo ao lado, atingir a paridade de funcionalidades e trocar num fim de semana. Enquanto isso, o sistema antigo continua mudando e a data da troca escorrega. Martin Fowler escreve que ele e seus colegas viram esse plano «ir por água abaixo na maioria das vezes».

O padrão strangler fig substitui o sistema peça por peça. Uma fachada na frente do monólito roteia cada requisição para o código antigo ou para um serviço novo, e as capacidades migram uma a uma até que nada seja roteado para o monólito. O roteamento é a parte fácil. Propriedade dos dados, prova de paridade e terminar o trabalho são as difíceis.

Este artigo cobre tudo isso, com um diagrama de uma migração em andamento.

De onde vem o nome

Em 2001, Fowler viu figueiras-mata-pau na floresta tropical de Queensland: trepadeiras que germinam sobre uma árvore hospedeira, criam raízes no solo e podem sobreviver à árvore, deixando a figueira com a forma dela. Em junho de 2004 ele as usou como metáfora para substituir um sistema legado aos poucos. Em abril de 2019 renomeou o artigo de «Strangler Application» para «Strangler Fig Application», para remeter à planta, e o reescreveu em agosto de 2024. A versão atual enfatiza o que as equipes resistem em aceitar: a arquitetura de transição, código que você escreve só para que o velho e o novo convivam.

A fachada faz o roteamento

A fachada é um reverse proxy ou API gateway na frente do monólito: NGINX, Envoy, um API gateway de nuvem ou um load balancer com regras de caminho. Ela começa enviando tudo ao monólito. Depois disso, cada passo da migração é uma mudança de roteamento. Roteie por:

  • Caminho: /catalog/* vai para o serviço de catálogo.
  • Método: GET /orders/* antes de POST /orders, para que as leituras migrem primeiro.
  • Percentual: um hash do ID de sessão ou de usuário mantém cada usuário de um só lado.
  • Header ou tenant: usuários internos ou um cliente piloto vão primeiro.
# upstream blocks catalog, orders and monolith defined elsewhere
split_clients "${cookie_session}" $orders_read {
    25%      orders;       # canary share for reads
    *        monolith;
}
map $request_method $orders_target {
    GET      $orders_read;
    default  monolith;     # order writes stay in the monolith
}
server {
    location /catalog/ { proxy_pass http://catalog; }
    location /orders/  { proxy_pass http://$orders_target; }
    location /         { proxy_pass http://monolith; }
}

O rollback é uma única edição: aponte a linha de 25% para monolith (o nginx rejeita uma entrada de 0%). Aplique o hash a uma chave que toda requisição carregue: requisições sem o cookie caem todas no mesmo bucket.

Mantenha a fachada enxuta: roteamento, TLS, autenticação e logging, sem regras de negócio. Ela fica em toda requisição, então rode várias instâncias e meça a latência que ela adiciona. O diagrama de arquitetura de API gateway mostra o que pertence a um gateway.

As quatro fases de uma migração

Fase A fachada roteia Dados Concluída quando
0. Fachada primeiro Tudo para o monólito Inalterados Fachada no ar, latência adicionada medida
1. Primeira capacidade Uma rota, como /catalog/*, para um serviço novo O serviço lê as tabelas do monólito ou uma cópia CDC, depois as próprias Rota a 100% durante um ciclo de negócio completo
2. Maior parte do tráfego em serviços novos A maioria das rotas; o monólito mantém uma lista conhecida Cada serviço é dono das suas tabelas O que sobrou cabe em uma página
3. Monólito aposentado Nada para o monólito Banco legado arquivado e depois apagado Monólito desligado

A página do padrão Strangler Fig da Microsoft acrescenta um passo final: remover a fachada, ou mantê-la como adaptador para clientes legados.

Escolhendo o que extrair primeiro

Bons primeiros candidatos têm três traços:

  • Uma costura clara, nas palavras de Michael Feathers um lugar onde você pode alterar o comportamento sem editar naquele lugar: um prefixo de URL, uma fila, o limite de um módulo.
  • Uma taxa de mudança alta, para que a migração compense logo. O controle de versão mostra isso.
  • Baixo acoplamento: poucos joins com outras tabelas, poucas chamadas de volta ao monólito.
git log --since="12 months ago" --format= --name-only -- src/ \
  | sort | uniq -c | sort -rn | head -20

Um ranking para uma loja online:

Candidato Costura Taxa de mudança Acoplamento Veredito
Navegação do catálogo /catalog/* Alta Baixo Primeiro
Histórico de pedidos GET /orders/* Média Médio Segundo, leituras primeiro
Checkout Chama preços, estoque e pagamentos Alta Alto Depois das suas dependências
Relatórios noturnos SQL sobre todo o esquema Baixa Muito alto Por último, ou um feed para um data warehouse

Extraia por capacidade de negócio, não por camada técnica. Um «serviço de acesso a dados» compartilhado não move nada e acopla tudo.

Movendo os dados

O roteamento move requisições, não dados, e é nos dados que as migrações travam. Decida quem é dono de cada tabela em cada passo.

Estratégia Como funciona Use para Risco
Banco compartilhado O serviço novo usa as tabelas do monólito As primeiras semanas Mudanças de esquema do monólito o quebram
Change data capture Um conector transmite as mudanças de linhas a partir do log do monólito Mover as leituras primeiro Atraso, esquemas legados, fluxo em uma só direção
Banco por serviço O serviço é dono das suas tabelas; os outros chamam sua API O estado final Joins entre serviços viram chamadas de API
Dupla escrita a partir do código A aplicação escreve nos dois bancos Nada Uma falha entre as escritas deixa os bancos dessincronizados

A orientação da Microsoft dá a ordem usual: o serviço novo começa nas tabelas do monólito, depois ganha o próprio banco, carregado uma vez e mantido atualizado por CDC, e vira o sistema de registro após a validação. Enquanto as tabelas legadas existirem, o rollback é uma mudança de rota; depois, é uma restauração.

Evite a dupla escrita: uma falha entre duas escritas separadas deixa os bancos em desacordo, e atualizações concorrentes podem chegar em ordens diferentes. Faça de uma única transação a origem de cada mudança, via CDC ou um transactional outbox. O diagrama de change data capture cobre o Debezium e os replication slots.

A camada anticorrupção

O modelo do monólito vaza: códigos de status de uma letra, campos de cobrança e entrega na mesma tabela, regras em stored procedures. Um serviço que consome essas linhas herda o modelo que você está deixando. Uma camada anticorrupção, um padrão do Domain-Driven Design de Eric Evans (2003), traduz na fronteira:

  • Legado para novo: eventos CDC com linhas legadas viram pedidos no modelo novo.
  • Novo para legado: o serviço de pedidos lê dados de clientes do monólito por meio de um adaptador.
  • Monólito para novo: depois que o catálogo migra, o código do monólito chama a API do catálogo em vez de suas tabelas antigas.

Mantenha regras de negócio fora da camada. Em um serviço hexagonal, ela vive nos adaptadores, de modo que o núcleo nunca vê tipos legados.

Provando a paridade antes de mover uma rota

Toda rota passa pelos mesmos passos:

  1. Shadow. A fachada copia as requisições para o serviço novo e descarta as respostas dele, como fazem o módulo mirror do NGINX e o request mirroring do Envoy. Espelhe só leituras: um POST /orders espelhado cria um segundo pedido.
  2. Comparar. Compare as duas respostas depois de normalizar timestamps, IDs e ordenação. O Diffy do Twitter, publicado como código aberto em 2015, roda duas cópias do código antigo e ignora como ruído o que difere entre elas. Dentro do monólito, a biblioteca Scientist do GitHub (Ruby, com ports para outras linguagens) executa lado a lado o caminho antigo e o novo e devolve o resultado do antigo.
  3. Canary. Mova 1%, 5%, 25%, 50% e depois 100% do tráfego real, compare a taxa de erros e a latência p99 com as do monólito para a mesma rota e mantenha cada passo durante um pico diário. O diagrama de estratégias de deploy cobre a mecânica.
  4. Manter. Em 100%, mantenha o caminho antigo implantável e seus dados atualizados durante eventos como um fechamento de fim de mês.
  5. Apagar o código antigo, suas tabelas e a rota.

Devolver uma rota só funciona enquanto o monólito tiver dados atuais. Quando o serviço novo for dono das escritas, devolva-as por CDC reverso ou eventos, ou um rollback as perderá.

Como as migrações strangler fig falham

  • Nunca terminar. Os 80% fáceis migram; relatórios, jobs em lote e telas de administração ficam, e você opera dois sistemas. Conte o que resta a cada sprint e defina uma data de desligamento.
  • Um monólito distribuído. Serviços que compartilham tabelas, fazem deploy juntos ou se chamam em longas cadeias síncronas. O teste é um escritor por tabela.
  • A fachada como gargalo: lógica de negócio no gateway, ou uma única instância carregando todo o tráfego.
  • Consumidores ocultos. Relatórios e outras equipes leem direto o banco do monólito, sem que a fachada os veja. Confira os logs de conexão antes de mover uma tabela.
  • Paridade de funcionalidades como meta. Fowler observa que muito do comportamento legado «não é realmente desejado». Patterns of Legacy Displacement cobre essa e outras armadilhas.

O diagrama da migração strangler fig

Clientes chamam um API gateway que faz de fachada. Ele envia /catalog/* (100 por cento) a um serviço de catálogo com banco próprio, 25 por cento de GET /orders/* a um serviço de pedidos e o resto ao monólito, cujo banco alimenta o de pedidos por um caminho CDC tracejado.

17 componentes · 10 conexões

Abrir no Archboard
Conteúdo do diagrama
  • Customers
  • Facade (API gateway)
  • Catalog service
  • Orders service
  • Legacy monolith
  • Catalog DB
  • Orders DB
  • Monolith DB
  • Debezium + Kafka
  • Anti-corruption layer
  • HTTPS
  • /catalog/* · 100%
  • GET /orders/* · 25%
  • everything else + 75% of GET /orders
  • reads, writes
  • reads
  • WAL changes
  • order row events
  • upserts in the new model
Uma migração de monólito em andamento. A fachada envia todo o tráfego do catálogo a um serviço novo dono dos seus dados, um quarto das leituras de pedidos a um serviço alimentado por change data capture e o resto ao monólito. Cada passo é reversível.

Como ler:

  • Da esquerda para a direita: os clientes chamam a fachada, que divide o tráfego por rota; os bancos de dados ficam à direita.
  • Verde, migrado: todo o /catalog/* vai para o serviço de catálogo, que é dono dos seus dados.
  • Âmbar, canary: 25% de GET /orders/* vai para o serviço de pedidos. As escritas e as outras leituras ainda vão para o monólito.
  • Cinza, legado: o monólito atende o resto e ainda é dono dos dados de pedidos.
  • Roxo, tracejado: o Debezium transmite as mudanças de pedidos do WAL do monólito pelo Kafka, e a camada anticorrupção as mapeia para o modelo novo.
  • A tabela de rotas lista destino, percentual e fase por rota, de modo que o rollback é uma única edição.

Desenhando migrações strangler fig com clareza

  • Um diagrama por fase, ou um título com data; guarde os antigos para mostrar a direção.
  • Rotule as rotas com caminho e percentual, como «GET /orders/* · 25%».
  • Mostre a propriedade dos dados: o sistema de registro e a direção de cada sincronização.
  • Colora por status (migrado, canary, legado), não por equipe.
  • Deixe de fora os detalhes internos. Isto é um mapa de migração, não um diagrama de componentes.
  • Desenhe os consumidores ocultos, como um relatório noturno que lê o banco do monólito.

O diagrama de arquitetura de microsserviços mostra como manter legível o estado final.

Monte no Archboard

  1. Entre com Google ou GitHub e crie um diagrama.
  2. Adicione uma Pessoa do pack C4 Model para os clientes e um Gateway de API do pack Nuvem para a fachada.
  3. Adicione três nós Contêiner do pack C4 Model: Catalog service, Orders service e Legacy monolith. Pinte-os de verde, âmbar e cinza.
  4. Adicione um nó Postgres do pack Dados à direita de cada contêiner.
  5. Entre o banco do monólito e o banco de pedidos, empilhe um nó Kafka do pack Dados (Debezium + Kafka) e um Componente do pack C4 Model (Anti-corruption layer), conectados para cima com arestas tracejadas.
  6. Desenhe as rotas a partir da fachada, rotuladas com caminho e percentual, e adicione acima de cada contêiner um rótulo de status Texto do pack Básico.
  7. Adicione um bloco Markdown do pack Básico com a tabela de rotas.

Se o seu mapa de rotas vive em um flowchart Mermaid, o conversor de Mermaid o transforma em um primeiro rascunho editável.

O exemplo usa 11 nós de diagrama e fica dentro do plano Free.

Perguntas frequentes

O que é o padrão strangler fig? Uma forma de substituir um sistema legado aos poucos: uma fachada roteia cada requisição para o código antigo ou para um serviço novo, as capacidades migram uma a uma e o sistema antigo é desligado quando nada é roteado para ele.

Por que se chama padrão strangler fig? Martin Fowler o batizou em 2004 por causa das figueiras-mata-pau que viu em Queensland, que crescem ao redor de uma árvore hospedeira e podem substituí-la. Ele acrescentou Fig ao nome em 2019 para destacar a planta.

Como lidar com o banco de dados em uma migração strangler fig? Em etapas. O serviço novo primeiro lê as tabelas do monólito, depois ganha o próprio banco alimentado por change data capture e assume as escritas após a validação. Evite a dupla escrita a partir do código da aplicação.

Qual é a diferença entre strangler fig e branch by abstraction? O strangler fig roteia requisições na borda do sistema. O branch by abstraction atua dentro do código: uma interface permite que a implementação antiga e a nova convivam até que todo chamador tenha migrado. Muitas migrações usam os dois.

Quando não usar o padrão strangler fig? Quando as requisições ao sistema antigo não podem ser interceptadas, quando o sistema é pequeno o bastante para ser substituído de uma vez ou quando precisa ser desligado rápido.

Comece grátis — um assento, sem cartão de crédito.

Artigos relacionados

← Voltar ao blog

Abra um canvas em um minuto

Entre com Google ou GitHub. Grátis para um assento. Faça upgrade quando a equipe entrar.