padrão strangler fig - Recomendações da AWS

padrão strangler fig

Intenção

O padrão strangler fig ajuda a migrar incrementalmente uma aplicação monolítica para uma arquitetura de microsserviços, com risco de transformação e interrupção dos negócios reduzidos.

Motivação

As aplicações monolíticas são desenvolvidas para fornecer a maior parte de suas funcionalidades em um único processo ou contêiner. O código está fortemente acoplado. Como resultado, as alterações na aplicação exigem um novo teste completo para evitar problemas de regressão. As mudanças não podem ser testadas isoladamente, o que afeta o tempo do ciclo. À medida que a aplicação é aprimorada com mais recursos, a alta complexidade pode levar a mais tempo gasto em manutenção, maior tempo de lançamento no mercado e, consequentemente, retardar a inovação do produto.

Quando a aplicação aumenta de tamanho, ela aumenta a carga cognitiva da equipe e pode ocasionar limites pouco claros de propriedade da equipe. Não é possível escalar recursos individuais com base na carga. Toda a aplicação precisa ser escalada para suportar picos de carga. À medida que os sistemas envelhecem, a tecnologia pode se tornar obsoleta, o que aumenta os custos de suporte. As aplicações monolíticas legadas seguem as práticas recomendadas que estavam disponíveis no momento do desenvolvimento e não foram projetadas para serem distribuídas.

Quando uma aplicação monolítica é migrada para uma arquitetura de microsserviços, ela pode ser dividida em componentes menores. Esses componentes podem ser escalados de forma independente, podem ser lançados de forma independente e podem ser de propriedade de equipes individuais. Isso resulta em uma maior velocidade de mudança, porque as alterações são localizadas e podem ser testadas e liberadas rapidamente. As mudanças têm um escopo de impacto menor porque os componentes têm acoplamento fraco e podem ser implantados individualmente.

Substituir completamente um monólito por uma aplicação de microsserviços reescrevendo ou refatorando o código é uma grande tarefa e um grande risco. Uma grande migração, em que o monólito é migrado em uma única operação, introduz risco de transformação e interrupção nos negócios. Enquanto a aplicação está sendo refatorada, é extremamente difícil ou mesmo impossível adicionar novos recursos.

Uma forma de resolver esse problema é usar o padrão fig pattern, introduzido por Martin Fowler. Esse padrão envolve migrar para microsserviços extraindo gradualmente recursos e criando uma nova aplicação em torno do sistema existente. Os recursos do monólito são substituídos gradualmente por microsserviços, e os usuários da aplicação podem usar os recursos recém-migrados progressivamente. Quando todos os recursos são transferidos para o novo sistema, a aplicação monolítica pode ser desativada com segurança.

Aplicabilidade

Use o padrão strangler fig quando:

  • Você deseja migrar gradualmente sua aplicação monolítica para uma arquitetura de microsserviços.

  • Uma abordagem de migração do big bang é arriscada devido ao tamanho e à complexidade do monólito.

  • A empresa quer adicionar novos recursos e não pode esperar que a transformação seja concluída.

  • Os usuários finais devem ser minimamente afetados durante a transformação.

Problemas e considerações

  • Acesso à base de código: para implementar o padrão strangler fig, você deve ter acesso à base de código da aplicação monolítica. À medida que os recursos são migrados do monólito, você precisará fazer pequenas alterações no código e implementar uma camada anticorrupção dentro do monólito para encaminhar chamadas para novos microsserviços. Você não pode interceptar chamadas sem acesso à base de código. O acesso à base de código também é essencial para redirecionar as solicitações recebidas. Talvez seja necessária alguma refatoração de código para que a camada de proxy possa interceptar as chamadas dos recursos migrados e roteá-las para microsserviços.

  • Domínio pouco claro: a decomposição prematura de sistemas pode ser cara, especialmente quando o domínio não está claro e é possível errar os limites do serviço. O design orientado por domínio (DDD) é um mecanismo para entender o domínio, e o event storming é uma técnica para determinar os limites do domínio.

  • Identificação de microsserviços: você pode usar o DDD como uma ferramenta essencial para identificar microsserviços. Para identificar microsserviços, procure as divisões naturais entre as classes de serviço. Muitos serviços terão seu próprio objeto de acesso a dados e se separarão facilmente. Serviços que têm lógica de negócios relacionada e classes que têm poucas ou nenhuma dependência são bons candidatos para microsserviços. Você pode refatorar o código antes de quebrar o monólito para evitar um acoplamento forte. Você também deve considerar os requisitos de conformidade, o ritmo de lançamento, a localização geográfica das equipes, as necessidades de escalabilidade, as necessidades de tecnologia orientadas por casos de uso e a carga cognitiva das equipes.

  • Camada anticorrupção: durante o processo de migração, quando os recursos dentro do monólito precisarem chamar os recursos que foram migrados como microsserviços, você deve implementar uma camada anticorrupção (ACL) que roteie cada chamada para o microsserviço apropriado. Para desacoplar e evitar alterações nos chamadores existentes dentro do monólito, a ACL funciona como um adaptador ou uma fachada que converte as chamadas na interface mais recente. Isso é discutido em detalhes na seção Implementação do padrão da ACL, anteriormente neste guia.

  • Falha na camada de proxy: durante a migração, uma camada de proxy intercepta as solicitações que vão para a aplicação monolítica e as roteia para o sistema legado ou para o novo sistema. No entanto, essa camada de proxy pode se tornar um único ponto de falha ou um gargalo de performance.

  • Complexidade da aplicação: monólitos grandes são os que mais se beneficiam do padrão strangler fig. Para aplicações pequenas, em que a complexidade da refatoração completa é baixa, talvez seja mais eficiente reescrever a aplicação na arquitetura de microsserviços em vez de migrá-la.

  • Interações de serviço: os microsserviços podem se comunicar de forma síncrona ou assíncrona. Quando a comunicação síncrona for necessária, considere se os tempos limite podem causar o consumo da conexão ou do grupo de threads, resultando em problemas de performance da aplicação. Nesses casos, use o padrão do disjuntor para retornar a falha imediata de operações que provavelmente falharão por longos períodos de tempo. A comunicação assíncrona pode ser obtida usando eventos e filas de mensagens.

  • Agregação de dados: em uma arquitetura de microsserviços, os dados são distribuídos entre bancos de dados. Quando a agregação de dados é necessária, você pode usar o AWS AppSync no frontend ou o padrão de segmentação de responsabilidade de consulta de comando (CQRS) no backend.

  • Consistência de dados: os microsserviços possuem seu armazenamento de dados, e a aplicação monolítica também pode provavelmente usar esses dados. Para permitir o compartilhamento, você pode sincronizar o armazenamento de dados dos novos microsserviços com o banco de dados da aplicação monolítica usando uma fila e um agente. No entanto, isso pode causar redundância de dados e eventual consistência entre dois armazenamentos de dados, por isso recomendamos que você a trate como uma solução tática até que você possa estabelecer uma solução de longo prazo, como um data lake.

Implementação

No padrão strangler fig, você substitui uma funcionalidade específica por um novo serviço ou aplicação, um componente por vez. Uma camada de proxy intercepta as solicitações que vão para a aplicatção monolítica e as roteia para o sistema legado ou para o novo sistema. Como a camada de proxy roteia os usuários para a aplicação correta, você pode adicionar recursos ao novo sistema e, ao mesmo tempo, garantir que o monólito continue funcionando. O novo sistema eventualmente substitui todos os recursos do sistema antigo, e você pode desativá-lo.

Arquitetura de alto nível

No diagrama a seguir, uma aplicação monolítica tem três serviços: serviço de usuário, serviço de carrinho e serviço de conta. O serviço de carrinho depende do serviço do usuário, e a aplicação usa um banco de dados relacional monolítico.

Aplicação monolítica com três serviços.

A primeira etapa é adicionar uma camada de proxy entre a interface do usuário do storefront e a aplicação monolítica. No início, o proxy roteia todo o tráfego para a aplicação monolítica.

Adição de um proxy à aplicação monolítica.

Quando quiser adicionar novos recursos à sua aplicação, você os implementa como novos microsserviços em vez de adicionar recursos ao monólito existente. No entanto, você continua corrigindo bugs no monólito para garantir a estabilidade da aplicação. No diagrama a seguir, a camada de proxy roteia as chamadas para o monólito ou para o novo microsserviço com base no URL da API.

Chamadas de roteamento de proxy para o monólito ou para um novo microsserviço.

Adição de uma camada anticorrupção

Na arquitetura a seguir, o serviço do usuário foi migrado para um microsserviço. O serviço de carrinho chama o serviço de usuário, mas a implementação não está mais disponível no monólito. Além disso, a interface do serviço recém-migrado pode não corresponder à interface anterior dentro da aplicação monolítica. Para lidar com essas alterações, você implementa uma ACL. Durante o processo de migração, quando os recursos do monólito precisam chamar os recursos que foram migrados como microsserviços, a ACL converte as chamadas na nova interface e as roteia para o microsserviço apropriado.

Adição de uma ACL para converter chamadas para a nova interface.

Você pode implementar a ACL dentro da aplicação monolítica como uma classe específica do serviço que foi migrado, por exemplo, UserServiceFacade ou UserServiceAdapter. A ACL deve ser desativada após a migração de todos os serviços dependentes para a arquitetura de microsserviços.

Quando você usa a ACL, o serviço de carrinho ainda chama o serviço do usuário dentro do monólito, e o serviço do usuário redireciona a chamada para o microsserviço por meio da ACL. O serviço de carrinho ainda deve chamar o serviço do usuário sem estar ciente da migração do microsserviço. Esse acoplamento fraco é necessário para reduzir a regressão e a interrupção dos negócios.

Gerenciamento da sincronização de dados

Como prática recomendada, o microsserviço deve possuir os dados. O serviço do usuário armazena seus dados em seu próprio armazenamento de dados. Talvez seja necessário sincronizar dados com o banco de dados monolítico para lidar com dependências, como relatórios, e oferecer suporte a aplicações downstream que ainda não estão prontas para acessar diretamente os microsserviços. A aplicação monolítica também pode exigir os dados de outras funções e componentes que ainda não foram migrados para microsserviços. Portanto, a sincronização de dados é necessária entre o novo microsserviço e o monólito. Para sincronizar os dados, você pode introduzir um agente de sincronização entre o microsserviço do usuário e o banco de dados monolítico, conforme mostrado no diagrama a seguir. O microsserviço do usuário envia um evento para a fila sempre que seu banco de dados é atualizado. O agente de sincronização escuta a fila e atualiza continuamente o banco de dados monolítico. Os dados no banco de dados monolítico acabam sendo consistentes com os dados que estão sendo sincronizados.

Adicionar um agente de sincronização.

Migração de serviços adicionais

Quando o serviço de carrinho é migrado da aplicação monolítica, seu código é revisado para chamar o novo serviço diretamente, de forma que a ACL não roteie mais essas chamadas. O diagrama a seguir ilustra esse cenário

Migração de serviços adicionais.

O diagrama a seguir mostra o estado final do processo de estrangulamento, em que todos os serviços foram migrados para fora do monólito, restando apenas o seu esqueleto. Os dados históricos podem ser migrados para armazenamentos de dados pertencentes a serviços individuais. A ACL pode ser removida e o monólito está pronto para ser desativado neste estágio.

Estado final do processo de estrangulamento após a migração de todos os serviços.

O diagrama a seguir mostra a arquitetura final após a desativação da aplicação monolítica. Você pode hospedar os microsserviços individuais por meio de um URL baseado em recursos (como http://www.storefront.com/user) ou por meio de seu próprio domínio (por exemplo, http://user.storefront.com) com base nos requisitos da sua aplicação. Para obter mais informações sobre os principais métodos para expor as APIs HTTP aos consumidores upstream usando nomes de host e caminhos, consulte a seção Padrões de roteamento de API.

Arquitetura final após o desativação do monólito.

Implementação usando serviços AWS

Uso do API Gateway como proxy da aplicação

O diagrama a seguir mostra o estado inicial da aplicação monolítica. Suponhamos que ela tenha sido migrada para a AWS usando uma estratégia de mover sem alterações (lift-and-shift), portanto, está sendo executada em uma instância do Amazon Elastic Compute Cloud (Amazon EC2) e usa um banco de dados do Amazon Relational Database Service (Amazon RDS). Para simplificar, a arquitetura usa uma única nuvem privada virtual (VPC) com uma sub-rede privada e uma pública, e vamos supor que os microsserviços serão inicialmente implantados na mesma Conta da AWS. (A melhor prática em ambientes de produção é usar uma arquitetura de várias contas para garantir a independência da implantação.) A instância do EC2 reside em uma única zona de disponibilidade na sub-rede pública, e a instância do RDS reside em uma única zona de disponibilidade na sub-rede privada. O Amazon Simple Storage Service (Amazon S3) armazena ativos estáticos, como arquivos JavaScript, CSS e React do site.

Estado inicial da aplicação monolítica ao usar o padrão strangler fig.

Na arquitetura a seguir, o AWS Migration Hub Refactor Spaces implanta o Amazon API Gateway na frente da aplicação monolítica. O Refactor Spaces cria uma infraestrutura de refatoração dentro da sua conta, e o API Gateway atua como a camada de proxy para rotear chamadas para o monólito. Inicialmente, todas as chamadas são roteadas para a aplicação monolítica por meio da camada de proxy. Conforme analisado anteriormente, as camadas de proxy podem se tornar um único ponto de falha. No entanto, usar o API Gateway como proxy reduz o risco porque é um serviço multi-AZ sem servidor.

Implementação do padrão strangler fig com o API Gateway.

O serviço do usuário é migrado para uma função do Lambda, e um banco de dados do Amazon DynamoDB armazena seus dados. Um endpoint de serviço do Lambda e uma rota padrão são adicionados ao Refactor Spaces, e o API Gateway é configurado automaticamente para rotear as chamadas para a função do Lambda.

Implementação do padrão strangler fig com o API Gateway: configuração do roteamento.

No diagrama a seguir, o serviço de carrinho também foi migrado do monólito para uma função do Lambda. Uma rota adicional e um endpoint de serviço são adicionados ao Refactor Spaces, e o tráfego passa automaticamente para a função Cart do Lambda. O armazenamento de dados da função do Lambda é gerenciado pelo Amazon ElastiCache. A aplicação monolítica ainda permanece na instância do EC2 junto com o banco de dados do Amazon RDS.

Migração de um serviço para fora do monólito com o padrão strangler fig.

No diagrama a seguir, o último serviço (conta) é migrado do monólito para uma função do Lambda. Ele continua usando o banco de dados original do Amazon RDS. A nova arquitetura agora tem três microsserviços com bancos de dados separados. Cada serviço usa um tipo diferente de banco de dados. Esse conceito de usar banco de dados com propósito específico para atender às necessidades específicas dos microsserviços é denominado persistência poliglota. As funções do Lambda também podem ser implementadas em diferentes linguagens de programação, conforme determinado pelo caso de uso. Durante a refatoração, o Refactor Spaces automatiza a substituição e o roteamento do tráfego para o Lambda. Isso economiza para seus criadores o tempo necessário para arquitetar, implantar e configurar a infraestrutura de roteamento.

Migração de todos os serviços para fora do monólito com o padrão strangler fig.

Uso de várias contas

Na implementação anterior, usamos uma única VPC com uma sub-rede pública e uma privada para a aplicação monolítica, e implantamos os microsserviços dentro da mesma Conta da AWS por uma questão de simplicidade. No entanto, este raramente é o caso em cenários do mundo real, em que os microsserviços geralmente são implantados em várias Contas da AWS para independência de implantação. Em uma estrutura de várias contas, você precisa configurar o tráfego de roteamento do monólito para os novos serviços em contas diferentes.

O Refactor Spaces ajuda você a criar e configurar a infraestrutura da AWS para rotear chamadas de API fora da aplicação monolítica. O Refactor Spaces orquestra o API Gateway, o Network Load Balancer e as políticas do AWS Identity and Access Management (IAM) baseadas em recursos nas suas contas da AWS como parte de seu recurso de aplicação. Você pode adicionar novos serviços de forma transparente em várias contas ou em uma única Conta da AWS a um endpoint HTTP externo. Todos esses recursos são orquestrados dentro da sua Conta da AWS e podem ser personalizados e configurados após a implantação.

Suponhamos que os serviços de usuário e carrinho de compras sejam implantados em duas contas diferentes, conforme mostrado no diagrama a seguir. Ao usar o Refactor Spaces, você só precisa configurar o endpoint do serviço e a rota. O Refactor Spaces automatiza a integração API Gateway–Lambda e a criação de políticas de recursos do Lambda, para que você possa se concentrar na refatoração segura de serviços fora do monólito.

Implementação do padrão strangler fig com o AWS Migration Hub Refactor Spaces.

Para ver um tutorial em vídeo sobre como usar o Refactor Spaces, consulte Refactor Apps Incrementally with AWS Migration Hub Refactor Spaces.

Workshop

Referências do blog

Conteúdo relacionado