Editor – Esta série de artigos em sete partes está concluída:
- Introdução aos microsserviços
- Criando microsserviços: usando um gateway de API
- Construindo microsserviços: comunicação entre processos em uma arquitetura de microsserviços
- Descoberta de serviços em uma arquitetura de microsserviços
- Gerenciamento de dados orientado a eventos para microsserviços
- Escolhendo uma estratégia de implantação de microsserviços
- Refatorando um monólito em microsserviços (este artigo)
Você também pode baixar a coleção completa de artigos, além de informações sobre a implementação de microsserviços usando o NGINX Plus, no formato de ebook – Microsserviços: do projeto à implantação. Confira também nossa série sobre a Arquitetura de referência de microsserviços e a página de soluções de microsserviços.
Este é o sétimo e último artigo da minha série sobre o desenvolvimento de aplicações com microsserviços. O primeiro artigo apresenta o padrão de arquitetura de microsserviços e discute as vantagens e desvantagens do uso de microsserviços. Os artigos seguintes abordam diferentes aspectos da arquitetura de microsserviços: uso de um gateway de API<.htmla>, comunicação entre processos, descoberta de serviços, gerenciamento de dados orientado a eventos e implantação de microsserviços. Neste artigo, analisamos estratégias para migrar uma aplicação monolítica para microsserviços.
Espero que esta série de artigos tenha proporcionado a você uma boa compreensão da arquitetura de microsserviços, de suas vantagens e desvantagens e de quando utilizá-la. Talvez a arquitetura de microsserviços seja uma boa opção para a sua organização.
No entanto, é bem provável que você esteja trabalhando em uma aplicação monolítica grande e complexa. Sua experiência diária de desenvolvimento e implantação da aplicação é lenta e penosa. Os microsserviços parecem um nirvana distante. Felizmente, existem estratégias que você pode usar para escapar do inferno monolítico. Neste artigo, descrevo como refatorar gradualmente uma aplicação monolítica, transformando-a em um conjunto de microsserviços.
Visão geral da refatoração para microsserviços
O processo de transformar uma aplicação monolítica em microsserviços é uma forma de modernização de aplicações. Isso é algo que os desenvolvedores vêm fazendo há décadas. Por isso, há algumas ideias que podemos reutilizar ao refatorar uma aplicação para microsserviços.
Uma estratégia que não deve ser usada é a reescrita “Big Bang”. Isso ocorre quando você concentra todos os seus esforços de desenvolvimento na criação do zero de uma nova aplicação baseada em microsserviços. Embora pareça atraente, essa abordagem é extremamente arriscada e provavelmente terminará em fracasso. Como Martin Fowler teria dito, “a única coisa que uma reescrita Big Bang garante é um Big Bang!”
Em vez de fazer uma reescrita Big Bang, você deve refatorar seu aplicativo monolítico de forma incremental. Aos poucos, você cria um novo aplicativo composto por microsserviços e o executa em conjunto com o aplicativo monolítico. Com o tempo, a quantidade de funcionalidades implementadas pelo aplicativo monolítico diminui até que ele desapareça por completo ou se torne apenas mais um microsserviço. Essa estratégia é como fazer a manutenção do carro enquanto você dirige por uma rodovia a 70 mph: é desafiador, mas muito menos arriscado do que tentar uma reescrita Big Bang.
Martin Fowler chama essa estratégia de modernização de aplicações de Aplicação Estranguladora. O nome vem da trepadeira estranguladora (também conhecida como figueira-estranguladora), encontrada em florestas tropicais. Ela cresce ao redor de uma árvore para alcançar a luz solar acima da copa da floresta. Às vezes, a árvore morre, deixando uma trepadeira com o formato dela. A modernização de aplicações segue o mesmo padrão. Criaremos uma nova aplicação composta por microsserviços ao redor da aplicação legada, que acabará desaparecendo.

Vamos analisar diferentes estratégias para fazer isso.
Estratégia 1 – Pare de cavar
A Lei dos Buracos diz que, sempre que você estiver em um buraco, deve parar de cavar. Esse é um ótimo conselho a seguir quando sua aplicação monolítica se torna incontrolável. Em outras palavras, você deve parar de aumentar o monólito. Isso significa que, ao implementar uma nova funcionalidade, você não deve adicionar mais código ao monólito. Em vez disso, a ideia central dessa estratégia é colocar esse novo código em um microsserviço independente. O diagrama a seguir mostra a arquitetura do sistema após a aplicação dessa abordagem.

Além do novo serviço e do monólito legado, há outros dois componentes. O primeiro é um roteador de requisições, que processa as requisições (HTTP) recebidas. Ele é semelhante ao gateway de API descrito em um artigo anterior<.htmla>. O roteador envia ao novo serviço as requisições correspondentes às novas funcionalidades. Já as requisições legadas são encaminhadas ao monólito.
O outro componente é o código de integração, que integra o serviço ao monólito. Um serviço raramente existe de forma isolada e muitas vezes precisa acessar dados pertencentes ao monólito. O código de integração, que reside no monólito, no serviço ou em ambos, é responsável pela integração dos dados. O serviço usa o código de integração para ler e gravar dados pertencentes ao monólito.
Há três estratégias que um serviço pode usar para acessar os dados do monólito:
- Invocar uma API remota fornecida pelo monólito
- Acessar diretamente o banco de dados do monólito
- Manter sua própria cópia dos dados, sincronizada com o banco de dados do monólito
O código de integração às vezes é chamado de camada anticorrupção. Isso porque o código de integração impede que o serviço, que tem seu próprio modelo de domínio intacto, seja contaminado por conceitos do modelo de domínio do monólito legado. O código de integração faz a tradução entre os dois modelos distintos. O termo camada anticorrupção apareceu pela primeira vez no livro de leitura obrigatória Domain Driven Design, de Eric Evans, e depois foi aprimorado em um white paper. Desenvolver uma camada anticorrupção pode ser uma tarefa nada trivial. Mas criá-la é essencial se você quiser escapar gradualmente do inferno monolítico.
Implementar uma nova funcionalidade como um serviço leve oferece algumas vantagens. Isso evita que o monólito se torne ainda mais difícil de gerenciar. O serviço pode ser desenvolvido, implantado e escalado de forma independente do monólito. Você aproveita os benefícios da arquitetura de microsserviços a cada novo serviço criado.
No entanto, essa abordagem não resolve os problemas do monólito. Para solucioná-los, é necessário decompor o monólito. Vejamos algumas estratégias para fazer isso.
Estratégia 2 – Separar o frontend do backend
Uma estratégia para reduzir a aplicação monolítica é separar a camada de apresentação das camadas de lógica de negócios e de acesso a dados. Uma aplicação empresarial típica consiste em pelo menos três tipos diferentes de componentes:
- Camada de apresentação – Componentes que processam solicitações HTTP e implementam uma API (REST) ou uma interface web baseada em HTML. Em uma aplicação com uma interface de usuário sofisticada, a camada de apresentação geralmente representa uma parte substancial do código.
- Camada de lógica de negócios – Componentes que constituem o núcleo da aplicação e implementam as regras de negócio.
- Camada de acesso a dados – Componentes que acessam componentes de infraestrutura, como bancos de dados e agentes de mensagens.
Geralmente, há uma separação clara entre a lógica de apresentação, de um lado, e as lógicas de negócios e de acesso a dados, do outro. A camada de negócios tem uma API de granularidade grossa composta por uma ou mais fachadas, que encapsulam os componentes da lógica de negócios. Essa API constitui um ponto natural pelo qual você pode dividir o monólito em duas aplicações menores. Uma aplicação contém a camada de apresentação. A outra contém as lógicas de negócios e de acesso a dados. Após a divisão, a aplicação da lógica de apresentação faz chamadas remotas à aplicação da lógica de negócios. O diagrama a seguir mostra a arquitetura antes e depois da refatoração.

Dividir um monólito dessa forma traz dois benefícios principais. Isso permite desenvolver, implantar e escalar as duas aplicações de maneira independente. Em particular, permite que os desenvolvedores da camada de apresentação façam iterações rápidas na interface do usuário e realizem testes A/B com facilidade, por exemplo. Outro benefício dessa abordagem é que ela disponibiliza uma API remota que pode ser chamada pelos microsserviços que você desenvolver.
No entanto, essa estratégia é apenas uma solução parcial. É muito provável que uma das aplicações, ou ambas, se torne um monólito difícil de gerenciar. Você precisa usar a terceira estratégia para eliminar o monólito ou os monólitos restantes.
Estratégia 3 – Extrair serviços
A terceira estratégia de refatoração consiste em transformar os módulos existentes no monólito em microsserviços independentes. Cada vez que você extrai um módulo e o transforma em um serviço, o monólito diminui. Depois que você tiver convertido módulos suficientes, o monólito deixará de ser um problema. Ele desaparecerá por completo ou ficará pequeno o bastante para ser apenas mais um serviço.
Priorizando quais módulos converter em serviços
Uma aplicação monolítica grande e complexa consiste em dezenas ou centenas de módulos, todos candidatos à extração. Muitas vezes, é difícil determinar quais módulos devem ser convertidos primeiro. Uma boa abordagem é começar por alguns módulos fáceis de extrair. Isso permitirá que você adquira experiência com microsserviços em geral e, em particular, com o processo de extração. Depois disso, extraia os módulos que proporcionarão o maior benefício.
Converter um módulo em um serviço geralmente é um processo demorado. É recomendável classificar os módulos de acordo com o benefício que você obterá. Em geral, é vantajoso extrair módulos que mudam com frequência. Depois de converter um módulo em um serviço, você poderá desenvolvê-lo e implantá-lo de forma independente do monólito, o que acelerará o desenvolvimento.
Também é vantajoso extrair módulos cujos requisitos de recursos sejam significativamente diferentes dos demais componentes do monólito. Por exemplo, pode ser útil transformar um módulo que tenha um banco de dados em memória em um serviço, que poderá então ser implantado em hosts com grandes quantidades de memória. Da mesma forma, pode valer a pena extrair módulos que implementem algoritmos com alto custo computacional, pois o serviço poderá então ser implantado em hosts com muitas CPUs. Ao transformar módulos com requisitos específicos de recursos em serviços, você pode facilitar muito o escalonamento da sua aplicação.
Ao decidir quais módulos extrair, é útil procurar limites de granularidade grossa já existentes (também conhecidos como pontos de separação). Eles tornam mais fácil e barato transformar módulos em serviços. Um exemplo desse tipo de limite é um módulo que se comunica com o restante da aplicação apenas por meio de mensagens assíncronas. Pode ser relativamente simples e barato transformar esse módulo em um microsserviço.
Como extrair um módulo
A primeira etapa para extrair um módulo é definir uma interface de granularidade grossa entre o módulo e o monólito. Muito provavelmente, será uma API bidirecional, pois o monólito precisará dos dados pertencentes ao serviço e vice-versa. Muitas vezes, é difícil implementar uma API desse tipo devido às dependências entrelaçadas e aos padrões de interação de granularidade fina entre o módulo e o restante da aplicação. A lógica de negócios implementada usando o padrão Modelo de Domínio é especialmente difícil de refatorar devido às inúmeras associações entre as classes do modelo de domínio. Muitas vezes, será necessário fazer alterações significativas no código para eliminar essas dependências. O diagrama a seguir mostra a refatoração.
Depois de implementar a interface de granularidade grossa, você transforma o módulo em um serviço independente. Para isso, é necessário escrever código que permita que o monólito e o serviço se comuniquem por meio de uma API que utiliza um mecanismo de comunicação entre processos (IPC). O diagrama a seguir mostra a arquitetura antes, durante e depois da refatoração.

Neste exemplo, o Módulo Z é o módulo candidato à extração. Seus componentes são usados pelo Módulo X, e ele usa o Módulo Y. A primeira etapa da refatoração é definir um par de APIs de granularidade grossa. A primeira interface é uma interface de entrada usada pelo Módulo X para invocar o Módulo Z. A segunda é uma interface de saída usada pelo Módulo Z para invocar o Módulo Y.
A segunda etapa da refatoração transforma o módulo em um serviço independente. As interfaces de entrada e saída são implementadas por um código que usa um mecanismo de IPC. Muito provavelmente, você precisará criar o serviço combinando o Módulo Z com um framework de chassi para microsserviços que lida com aspectos transversais, como a descoberta de serviços.
Depois de extrair um módulo, você passa a ter mais um serviço que pode ser desenvolvido, implantado e escalado de forma independente do monólito e de quaisquer outros serviços. Você pode até mesmo reescrever o serviço do zero; nesse caso, o código da API que integra o serviço ao monólito se torna uma camada anticorrupção que faz a tradução entre os dois modelos de domínio. Cada vez que você extrai um serviço, dá mais um passo em direção aos microsserviços. Com o tempo, o monólito diminuirá e você terá um número cada vez maior de microsserviços.
Resumo
O processo de migração de uma aplicação existente para microsserviços é uma forma de modernização de aplicações. Você não deve migrar para microsserviços reescrevendo sua aplicação do zero. Em vez disso, deve refatorá-la gradualmente, transformando-a em um conjunto de microsserviços. Há três estratégias que podem ser usadas: implementar novas funcionalidades como microsserviços; separar os componentes de apresentação dos componentes de negócios e de acesso a dados; e converter módulos existentes do monólito em serviços. Com o tempo, o número de microsserviços aumentará, assim como a agilidade e a velocidade da sua equipe de desenvolvimento.
Editor – Esta série de artigos em sete partes está concluída:
- Introdução aos microsserviços
- Criando microsserviços: como usar um gateway de API
- Construindo microsserviços: comunicação entre processos em uma arquitetura de microsserviços
- Descoberta de serviços em uma arquitetura de microsserviços
- Gerenciamento de dados orientado a eventos para microsserviços
- Escolhendo uma estratégia de implantação de microsserviços
- Refatorando um monólito em microsserviços (este artigo)
Você também pode baixar o conjunto completo de artigos, além de informações sobre como implementar microsserviços usando o NGINX Plus, no formato de ebook – Microsserviços: do projeto à implantação. Confira também nossa série sobre a Arquitetura de referência de microsserviços e a página de soluções para microsserviços.
O blogueiro convidado Chris Richardson é o fundador da plataforma original CloudFoundry.com, uma das primeiras PaaS (plataforma como serviço) Java para o Amazon EC2. Atualmente, ele presta consultoria a organizações para aprimorar a forma como desenvolvem e implantam aplicações. Ele também publica regularmente sobre microsserviços em https://microservices.io
Sobre o autor
Publicações relacionadas no blog

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade
Descubra como o F5 NGINX One aprimora o gerenciamento e a observabilidade de ambientes híbridos e multicloud ao oferecer um console web centralizado baseado em SaaS, com insights em tempo real, conformidade automatizada e gerenciamento simplificado de configurações.

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa
Descubra como transformar uma definição de esquema OpenAPI em uma configuração NGINX totalmente funcional, operando como um API Gateway com a segurança de um Web Application Firewall e um Portal do Desenvolvedor, usando uma abordagem declarativa de API.

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor
Picos de tráfego e sobrecarga de servidores são preocupações sérias para varejistas online em qualquer época, mas especialmente durante a “Cyber Season” de compras de fim de ano. Detalhamos como você pode aproveitar as soluções de entrega de aplicações de nível de produção da NGINX para se preparar para aumentos repentinos de tráfego.

Distribuição segura de chaves privadas SSL com o NGINX
Descrevemos três maneiras progressivamente mais seguras de proteger chaves privadas SSL ao configurar o NGINX para processar tráfego HTTPS: permitir acesso de leitura apenas ao usuário root, criptografar as chaves com senhas armazenadas separadamente e distribuir as senhas a partir de um repositório central.
Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX
O módulo JavaScript do NGINX lançado com o NGINX Plus R15 oferece suporte a subsolicitações, possibilitando uma classe totalmente nova de casos de uso para o NGINX Plus. Este exemplo completo também usa a API do NGINX Plus e os módulos de chave-valor para converter solicitações de API dos clientes em solicitações separadas para as APIs dos microsserviços que compõem um aplicativo.
Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus
Confira dicas para evitar o esgotamento de portas efêmeras com ajustes no kernel do Linux, conexões keepalive e conexões vinculadas dinamicamente a endereços IP locais.