No mundo digital acelerado de hoje, onde agilidade e escalabilidade são cruciais, as empresas estão constantemente buscando maneiras de melhorar o desempenho e a capacidade de manutenção de seus aplicativos da web.
Uma abordagem popular para alcançar esses objetivos é migrar de uma arquitetura monolítica para uma distribuída (ou micro-frontend). Esta série de artigos, “Micro-frontend Migration Journey”, compartilha minha experiência pessoal ao realizar tal migração durante meu tempo na AWS.
ISENÇÃO DE RESPONSABILIDADE : Antes de começarmos, é importante observar que, embora este artigo compartilhe minha experiência pessoal, não posso divulgar nenhum detalhe proprietário ou interno de ferramentas, tecnologias ou processos específicos na AWS ou em qualquer outra organização.
Estou empenhado em respeitar as obrigações legais e garantir que este artigo se concentre apenas nos conceitos e estratégias gerais envolvidos na jornada de migração de microfrontend.
O objetivo é fornecer insights e lições aprendidas que possam ser aplicáveis em um contexto mais amplo, sem divulgar nenhuma informação confidencial.
Aprendi sobre micro-frontends (acho que como muitos de vocês) no artigo do blog de Martin Fowler. Ele apresentou diferentes maneiras de compor a arquitetura de micro-frontend de maneira independente de framework.
Conforme me aprofundei no assunto, percebi que nossa arquitetura monolítica existente estava se tornando um gargalo significativo para a produtividade de nossa equipe e impedindo o desempenho geral de nosso aplicativo.
Um dos principais fatores que me levou a considerar uma migração foi o aumento do tamanho do pacote de nosso aplicativo.
Depois de conduzir uma análise completa do pacote no verão de 2020, descobri que desde seu lançamento inicial no início de 2019, o tamanho do pacote (gzipado) aumentou de 450 KB para 800 KB (quase 4 MB analisados) - quase o dobro do tamanho original.
Considerando o sucesso de nosso serviço e prevendo seu crescimento contínuo, ficou claro que essa tendência persistiria, impactando ainda mais o desempenho e a manutenção de nosso aplicativo.
Embora estivesse entusiasmado com o conceito de micro-frontends, também reconheci que ainda não estávamos prontos para adotá-los devido a desafios específicos que enfrentamos:
Estrutura organizacional pequena: na época da minha análise, nossa organização era relativamente pequena e eu era o único engenheiro de front-end em tempo integral na equipe. A migração para uma arquitetura de microfrontend exigiu um investimento significativo em termos de estrutura organizacional e base operacional.
Era crucial ter uma estrutura madura que pudesse efetivamente lidar com a arquitetura distribuída e refletir as dependências entre os diferentes componentes frontend.
Domínio de negócios limitado: embora os microfrontends possam ser divididos com base em contextos limitados e recursos de negócios (saiba mais no post “ Design orientado a domínio na arquitetura de microfrontend” ), nosso domínio de negócios principal não era extenso o suficiente para justificar uma separação completa em vários micro-frontends. No entanto, havia limites visíveis dentro do aplicativo que faziam sentido esculpir e fazer a transição para uma arquitetura distribuída.
Considerando esses fatores, percebi que uma abordagem gradual era necessária. Em vez de uma migração completa para micro-frontends, meu objetivo era identificar áreas específicas em nosso aplicativo que poderiam se beneficiar de uma arquitetura distribuída.
Isso nos permitiria abordar questões de desempenho e escalabilidade sem interromper a estrutura organizacional geral ou comprometer a integridade de nosso domínio de negócios. Também nos daria algum tempo para aumentar a equipe e observar as direções do negócio.
Observe que, se você deseja resolver o problema de desempenho do aplicativo (tamanho do pacote) apenas usando a arquitetura mciro-frontend, pode não ser a melhor ideia. Seria melhor começar com uma arquitetura monolítica distribuída que aproveitaria o carregamento lento (importações dinâmicas).
Além disso, acho que lidaria com problemas de tamanho de pacote de maneira mais elegante do que a arquitetura de microfrontend, considerando que é muito provável que a arquitetura de microfrontend tenha algum código compartilhado que não seria separado em partes do fornecedor e seria incorporado ao pacote de aplicativos ( esse é um dos contras dessa arquitetura distribuída — você precisa ter uma compensação entre o que compartilhar, quando e como).
No entanto, a arquitetura monolítica distribuída não será dimensionada tão bem quanto o microfrontend. Quando sua organização cresce rapidamente, sua equipe provavelmente também crescerá no mesmo ritmo.
Haveria uma necessidade essencial de dividir a base de código em diferentes áreas de propriedade controladas por diferentes equipes.
E cada equipe precisará ter seus próprios ciclos de lançamento independentes dos outros, cada equipe apreciará se sua base de código for focada exclusivamente em seu domínio e será construída rapidamente (isolamento de código -> melhor capacidade de manutenção/menos código para manter e build -> melhor testabilidade/menos teste para manter e executar).
Para obter o apoio da liderança, elaborei um documento de visão técnica persuasiva que englobava uma análise de desempenho abrangente, incluindo métricas vitais da web, e descrevia as várias fases da migração para front-ends distribuídos.
Uma das fases intermediárias dessa migração foi estabelecer uma arquitetura monolítica distribuída, na qual vários módulos/widgets poderiam ser entregues de forma assíncrona por meio de técnicas de carregamento lento, aproveitando a infraestrutura compartilhada, como um balde S3 e CDN, entre o serviço principal e os widgets .
Como descrevi em meu artigo anterior, a ideia principal desse tipo de documento é descrever o futuro como você gostaria que fosse, uma vez que os objetivos foram alcançados e os maiores problemas resolvidos. Não é sobre o plano de execução!
Quase 1 ano depois, finalmente chegou a hora de colocar meu plano de migração de micro-frontend em ação. Com a expansão iminente para um novo domínio e uma equipe maior à nossa disposição, estávamos bem equipados para executar a migração.
Parecia uma oportunidade de ouro que não podíamos perder.
Afinal, permanecer confinado à arquitetura monolítica significaria lutar perpetuamente com suas limitações.
O cronograma limitado para expandir para um novo domínio serviu como um catalisador, impulsionando-nos a construir uma arquitetura mais escalável e sustentável imediatamente, em vez de ter iterações curtas e lentas!
Para executar a migração e simultaneamente lidar com o trabalho no novo domínio, dividimos as equipes em dois grupos dedicados. O trabalho de recurso, que tinha maior prioridade, exigia mais recursos e precisava iterar em um ritmo mais rápido.
Para garantir a integridade e compreensão abrangente do processo de migração, fazia sentido designar uma pequena equipe dedicada especificamente responsável por lidar com a migração.
No entanto, não poderíamos prosseguir com o trabalho do recurso sem primeiro garantir que o conceito de microfrontend fosse bem-sucedido.
Para mitigar os riscos e fornecer um roteiro claro, era crucial criar um documento de design de baixo nível que incluísse estimativas precisas e uma avaliação de risco completa. Este documento serviu como um modelo, descrevendo as etapas e considerações necessárias para a migração.
O marco fundamental nesse processo foi o desenvolvimento de uma prova de conceito que demonstraria a integração bem-sucedida de todos os componentes de acordo com o projeto.
Este marco, apropriadamente chamado de “Ponto sem retorno”, teve como objetivo validar a viabilidade e a eficácia da arquitetura de micro-frontend.
Embora eu estivesse otimista com o sucesso da migração, era essencial me preparar para as contingências. Consequentemente, elaborei um Plano B, que funcionou como uma estratégia de backup caso o conceito inicial não produzisse os resultados desejados.
Isso incluiu alocar sete dias adicionais nas estimativas especificamente para me fazer chorar no travesseiro, mais alguns dias para ter uma nova entrada de módulo de recurso conectada ao núcleo por meio de carregamento lento (lembra-se do monólito distribuído?).
Ao projetar micro-frontends, geralmente existem 3 abordagens para composição, cada uma focando onde ocorre a resolução do aplicativo em tempo de execução. A beleza dessas abordagens é que elas não são mutuamente exclusivas e podem ser combinadas conforme necessário.
A ideia básica é aproveitar um servidor proxy reverso para dividir os pacotes de micro-frontend por página e fazer um recarregamento físico da página com base na URL da rota.
Prós:
Contras:
O estado global não será sincronizado entre os aplicativos de microfrontend. Este foi um claro ponto proibido para nós, porque tínhamos operações de segundo plano de longa duração executadas no lado do cliente.
Você pode argumentar que poderíamos manter um instantâneo da “fila” dessa operação no armazenamento local e lê-lo após o hard-reload, mas, devido a motivos de segurança, não conseguimos implementar isso.
Este é apenas um exemplo de um estado global, mas aqui está outro exemplo de como pode ser: estado dos painéis de navegação lateral (expandido/recolhido), mensagens do sistema, etc.
Outra abordagem para composição de microfrontend é a composição de borda, que envolve a combinação de microfrontends na camada de borda, como um CDN. Por exemplo, o Amazon CloudFront oferece suporte à integração Lambda@Edge , permitindo o uso de um CDN compartilhado para ler e servir o conteúdo do microfrontend.
Prós:
Contras:
A composição do lado do cliente é outra abordagem para a arquitetura de microfrontend que utiliza técnicas de orquestração de microfrontend do lado do cliente, dissociadas da implementação do servidor.
O principal participante dessa arquitetura é um aplicativo de contêiner (shell) que tem as seguintes responsabilidades:
A ideia geral é que cada pacote de micro-frontend produziria 2 tipos de arquivos assets:
{hash}/index.js: serve como ponto de entrada para o aplicativo de microfrontend, com o hash representando um identificador exclusivo para toda a compilação.
O hash atua como uma chave de prefixo para cada pacote no bucket S3. É importante observar que podem existir vários pontos de entrada, mas o hash permanece o mesmo para todos eles.
manifest.json: Este é um arquivo de manifesto que contém caminhos para todos os pontos de entrada para o aplicativo de microfrontend. Esse arquivo sempre sairia na raiz do bucket S3, assim o container conseguiria descobri-lo facilmente.
Recomendo ativar o versionamento deste arquivo no bucket do S3 para ter uma melhor observabilidade das mudanças. Se você estiver usando o Webpack para criar seu projeto, recomendo o WebpackManifestPlugin , que faz todo o trabalho pesado para você.
O contêiner está ciente apenas da URL do domínio de origem do recurso de microfrontend (origem da CDN) com base no estágio e na região. Durante o carregamento inicial da página, o contêiner baixa o arquivo de manifesto para cada aplicativo de microfrontend.
O arquivo de manifesto tem um tamanho pequeno (cerca de 100 bytes) para evitar o impacto no tempo de carregamento da página e é bem dimensionado mesmo ao agregar vários micro-frontends em um contêiner. É crucial considerar o arquivo de manifesto como imutável no armazenamento de cache do navegador para evitar cache agressivo.
Escolher a biblioteca de orquestração certa é o maior desafio nessa composição e será discutido no próximo capítulo.
Prós:
Contras:
Como mencionei anteriormente neste capítulo, todos esses padrões de composição podem ser misturados e combinados dentro do mesmo aplicativo shell. Aqui está um exemplo de como pode ser:
Eu recomendo começar com uma abordagem homogênea no começo — selecione um padrão de composição que seja mais adequado para você e comece a construir a infraestrutura em torno dele.
Para nós, a composição do lado do cliente era a melhor opção, mas, para o futuro, consideramos mudar algumas regiões para a orquestração do lado da borda (com base na disponibilidade do Lambda@Edge).
Quando se trata de implementar a composição do lado do cliente em uma arquitetura de microfrontend, selecionar a biblioteca de orquestração certa é uma decisão crítica.
A biblioteca escolhida desempenhará um papel crucial no gerenciamento do carregamento dinâmico e na coordenação de microfrontends no aplicativo de contêiner.
Existem várias bibliotecas de orquestração populares, cada uma com seus próprios pontos fortes e considerações.
Single-spa é uma biblioteca de orquestração amplamente adotada que fornece uma abordagem flexível e extensível para composição de microfrontend. Ele permite que os desenvolvedores criem um aplicativo shell que orquestra o carregamento e o descarregamento de vários microfrontends.
O Single-SPA fornece controle refinado sobre eventos de ciclo de vida e oferece suporte a diferentes estruturas e tecnologias.
Prós:
Contras:
Qiankun é uma poderosa biblioteca de orquestração desenvolvida pela equipe Ant Financial (Alibaba). Ele usa uma abordagem HTML parcial para composição. No lado do aplicativo micro-frontend, ele produz um trecho de HTML simples com todos os pontos de entrada a serem carregados.
Depois de consumir esse arquivo HTML, o contêiner faz toda a orquestração e monta o aplicativo. Nesta configuração, o HTML parcial desempenha o papel de um arquivo de manifesto sobre o qual falei no capítulo anterior.
Prós:
Contras:
A Federação de Módulos , um recurso fornecido pelo Webpack, ganhou atenção e destaque significativos na comunidade de desenvolvimento da web. Essa tecnologia permite que os desenvolvedores compartilhem código entre vários aplicativos em tempo de execução, tornando-se uma opção atraente para a construção de micro-frontends.
Com sua integração perfeita com Webpack e flexibilidade de tempo de execução, o Module Federation tornou-se uma escolha popular para gerenciar e orquestrar micro-frontends.
Prós:
Contras:
Nesta primeira parte da série “Micro-frontend Migration Journey”, discutimos a motivação por trás da migração de um monólito da web para uma arquitetura distribuída e as etapas iniciais tomadas para vender a ideia à liderança.
Exploramos a importância de um documento de visão técnica que apresentasse uma análise de desempenho detalhada e descrevesse as diferentes fases da migração.
Em seguida, nos aprofundamos nas considerações de design para microfrontends, discutindo três abordagens: composição do lado do servidor, composição do lado da borda e composição do lado do cliente.
Cada abordagem tem seus prós e contras, e a escolha depende de vários fatores, como sincronização do estado global, experiência do cliente, complexidade da infraestrutura e armazenamento em cache.
Além disso, exploramos bibliotecas de orquestração populares, como single-spa, qiankun e Module Federation, destacando seus recursos, benefícios e possíveis desafios.
Junte-se a mim nas próximas partes da série enquanto continuamos nossa jornada de migração de microfrontend, descobrindo insights mais interessantes e valiosos ao longo do caminho!
Originalmente publicado em https://thesametech.com em 18 de abril de 2023.
Você também pode me seguir no Twitter e se conectar no LinkedIn para receber notificações sobre novos posts!