Software · Soluções na Prática

Modernização de Sistemas Legados: Estratégias Práticas sem Reescrita Big Bang

Como substituir sistemas legados de forma incremental usando o padrão Strangler Fig, camadas anticorrupção (ACL) e Branch by Abstraction sem interromper a operação.

Diagrama conceitual do padrão Strangler Fig demonstrando a interceptação na borda e substituição gradual de módulos legados
Neste artigo

A tentativa de modernizar sistemas corporativos legados por meio de uma reescrita completa e abrupta (conhecida como Big Bang Rewrite) figura historicamente entre os maiores causadores de prejuízos em engenharia de software. Interromper o lançamento de novas funcionalidades no negócio durante meses ou anos para reescrever uma aplicação do zero subestima a quantidade massiva de regras implícitas e casos especiais acumulados no código antigo. Na data agendada para a virada de chave, o novo sistema raramente contempla a complexidade real da operação e o projeto descamba para atrasos severos ou cancelamento.

A alternativa moderna comprovada pela indústria substitui a ruptura pelo estrangulamento incremental. Inspirada na metáfora botânica da figueira-estranguladora observada por Martin Fowler, essa abordagem constrói o novo sistema em torno do antigo, capturando fluxos aos poucos até que o legado possa ser desligado com segurança e sem impacto para os clientes.

A Arquitetura do Padrão Strangler Fig

O padrão Strangler Fig opera na camada de tráfego de rede e roteamento perimetral. Em vez de exigir que clientes externos aprendam novos contratos ou saibam da existência de múltiplos sistemas, um proxy reverso ou API Gateway é posicionado à frente da infraestrutura existente:

flowchart TD
    Clientes["Clientes Web / Mobile / Integrações"]
    Gateway["Proxy Reverso / API Gateway<br/>(Fachada Estranguladora)"]

    Novo["NOVO SERVIÇO<br/>(Arquitetura Moderna)"]
    Legado["SISTEMA LEGADO<br/>(Monólito / Banco Legado)"]

    ACL["Anti-Corruption Layer (ACL)<br/>(Tradução e Sincronização)"]

    Clientes --> Gateway
    Gateway -->|"Rotas Novas Migradas"| Novo
    Gateway -->|"Rotas Legadas Originais"| Legado
    Novo <-->|"Tradução de Contratos"| ACL
    Legado <-->|"Acesso a Dados Legados"| ACL

O Ciclo de Vida da Transição

  1. Interceptação Transparente: Todas as requisições passam pela Fachada Estranguladora (Strangler Facade), que inicialmente despacha 100% do tráfego para a aplicação legada.
  2. Criação da Nova Capacidade: Uma funcionalidade específica e bem delimitada (por exemplo, o cálculo de frete ou a autenticação de usuários) é desenvolvida do zero em uma arquitetura limpa, moderna e testável.
  3. Desvio Gradual de Tráfego: A regra de roteamento no gateway é alterada para encaminhar as chamadas daquela rota específica para o novo serviço, mantendo as demais rotas no legado.
  4. Descomissionamento Final: Conforme os módulos antigos perdem responsabilidades operacionais, o código legado atrofia gradualmente até transformar-se em uma casca vazia que pode ser desligada com risco controlado.

Protegendo o Domínio com a Anti-Corruption Layer (ACL)

Durante os meses em que o sistema novo e o sistema legado convivem em produção, o novo serviço inevitavelmente precisará consultar clientes, pedidos ou tabelas mantidas no armazenamento antigo. O maior perigo nesse período é permitir que as decisões arcaicas de modelagem do legado (nomes crípticos de colunas, chaves compostas despadronizadas ou regras misturadas) contaminem as classes do novo software.

A Anti-Corruption Layer (Camada Anticorrupção) atua como um tradutor bidirecional de fronteira entre os dois mundos:

// 1. Interface pura do novo domínio moderno
public interface IConsultaClienteLegacy
{
    Task<ClienteModerno?> ObterClientePorDocumentoAsync(string cpf);
}

// 2. Implementação da ACL que isola as esquisitices do legado
public class LegacyClientAntiCorruptionLayer : IConsultaClienteLegacy
{
    private readonly ILegacySoapClient _soapClient;

    public LegacyClientAntiCorruptionLayer(ILegacySoapClient soapClient) => _soapClient = soapClient;

    public async Task<ClienteModerno?> ObterClientePorDocumentoAsync(string cpf)
    {
        // Chama o endpoint SOAP legado com payloads antigos
        var legacyDto = await _soapClient.BuscarRegistroClienteAsync(new LegacyParam { NR_DOC = cpf });
        if (legacyDto == null || legacyDto.STATUS_FLG != "A") return null;

        // Traduz e sanitiza os dados para a entidade moderna e rica
        return new ClienteModerno(
            id: Guid.Parse(legacyDto.CD_UUID),
            nome: legacyDto.NM_RAZAO_SOCIAL.Trim(),
            cpf: legacyDto.NR_DOC,
            ativo: true
        );
    }
}

O novo serviço consome apenas a abstração limpa. Caso a estrutura do banco legado mude, apenas a classe da ACL é ajustada, preservando a higidez do novo modelo.

Modernização Interna: Branch by Abstraction

Quando o objetivo é substituir um módulo interno altamente acoplado dentro de um monólito sem a viabilidade imediata de separá-lo em um serviço de rede, aplica-se o padrão Branch by Abstraction:

  1. Uma interface abstrata é declarada em frente ao componente interno obsoleto.
  2. Todo o código do monólito é refatorado para consumir exclusivamente a interface abstrata.
  3. A nova implementação moderna é construída em paralelo satisfazendo a mesma interface.
  4. Um mecanismo de controle de funcionalidade (Feature Toggle) comuta a execução em tempo de execução para a nova implementação, permitindo testes graduais e reversão instantânea em caso de divergência.
public class RepositorioUsuarioProxy : IRepositorioUsuario
{
    private readonly IRepositorioUsuario _legado;
    private readonly IRepositorioUsuario _novo;

    public RepositorioUsuarioProxy(RepositorioLegado legado, RepositorioNovo novo)
    {
        _legado = legado;
        _novo = novo;
    }

    public async Task<Usuario> ObterAsync(int id)
    {
        // Alternância segura baseada em configuração ou porcentagem de usuários
        if (FeatureManagement.HabilitarNovoMotorPersistencia)
        {
            return await _novo.ObterAsync(id);
        }
        return await _legado.ObterAsync(id);
    }
}

Estratégias Práticas para Migração Strangler Fig

Em iniciativas de transformação arquitetural em grandes empresas, a modernização gradual costuma seguir um roteiro técnico bem delimitado:

  • Começar pelas bordas: Nunca iniciar pela entidade central mais conectada do sistema. Iniciar por fluxos periféricos de baixo risco e alto valor (como relatórios ou notificações) para validar a esteira de entrega contínua e as regras do gateway.
  • Sincronização passiva por eventos: Utilizar Change Data Capture (CDC) no banco legado para replicar dados em tempo real para os novos armazenamentos, permitindo testes comparativos (shadow traffic) sem risco de corrupção de dados.
  • Transparência para a diretoria e usuários: Ao entregar melhorias funcionais a cada iteração quinzenal em vez de um hiato de dois anos, a liderança percebe retorno tangível sobre o investimento contínuo.
Explorar este tema no Grafo de Conhecimento