Nos últimos anos, a comunidade front-end tem discutido ativamente e usado o termo “micro-front-end” (doravante referido como MF). Diferentes empresas compartilham suas abordagens para organizar tal solução arquitetônica , mas até agora há pouca descrição dos problemas que os MFs são projetados para resolver na Web, os critérios para sua aplicabilidade e as limitações de uso.
Neste artigo, tentei comparar diferentes formas de organizar MFs, bem como formular recomendações sobre onde usar qual abordagem.
O artigo pode ser útil tanto para analistas e equipes de desenvolvimento ao projetar a arquitetura de um projeto e definir processos, quanto para proprietários de produtos, pois a introdução de MFs pode fornecer um desenvolvimento mais gerenciável.
Antes de passarmos para a definição de MF, vejamos alguns problemas que podem ser encontrados em projetos:
Você tem um grande projeto . O tamanho de um projeto geralmente é subjetivo e pode ser determinado empiricamente pela quantidade de funcionalidade e pelo número de desenvolvedores. Se você tiver trabalho suficiente para quebrar 1-2 front-enders e ao mesmo tempo eles não "empurrarem os cotovelos" - este é um projeto pequeno, 3-6 é médio e mais de 6-8 já é grande .
Você tem uma grande equipe . Novamente, empiricamente, são mais de 10 front-enders, o restante dos participantes não conta. Via de regra, uma equipe desse porte já pode ser dividida em subequipes que assumem funcionalidades específicas para suporte, e adquirem seus próprios analistas, backenders e QA.
Você tem uma grande funcionalidade. Um único desenvolvedor pode manter apenas um pedaço de código para sua subequipe. Refinar o restante do código pode ser caro devido ao desconhecimento da área de assunto ou à complexidade da implementação da lógica de terceiros.
o desejo de mudar a pilha;
uma exigência da empresa para apoiar uma família de projetos relacionados.
Como você pode organizar as relações entre tantas pessoas? Como você pode construir processos em um projeto desta escala? Como você pode delinear adequadamente as áreas de responsabilidade? Quanto mais problemas você coletar, mais valerá a pena considerar a introdução de uma abordagem microfrontal. Visto que esta é uma continuação natural da tendência evolutiva do desenvolvimento em direção à decomposição do código e da equipe do projeto.
Assim, a abordagem MF é a divisão de uma frente monolítica em bases de código separadas armazenadas em repositórios separados, aos quais subequipes separadas têm acesso. Ao mesmo tempo, eles podem/devem ter seus próprios estandes de demonstração, testes e ciclos de lançamento. Assim, o microfront é uma peça destacável da interface. Não é necessário dividir por página, a funcionalidade pode ser ponta a ponta (por exemplo, ui-kit corporativo).
Separadamente, vale a pena destacar que os MFs são mais uma decisão organizacional sobre como gerenciar a complexidade do desenvolvimento em um grande projeto. MFs não vão te ajudar a acelerar o frontend, algumas implementações, pelo contrário, vão até desacelerá-lo. Mas essa abordagem acelerará o próprio desenvolvimento devido à alocação de áreas de responsabilidade e testes isolados.
Por outro lado, vale a pena mencionar o Lazy loading em comparação com os MFs. Eles resolvem problemas diferentes, mas às vezes as pessoas pensam que se trata de uma coisa só, porque em ambos os casos "dividimos" o aplicativo.
O carregamento lento resolve o problema de desempenho: como não forçar o usuário a carregar todo o pacote front-end, como não esperar mais do que o necessário e como iniciar o front no cliente mais rapidamente e começar a interagir com ele mais cedo.
Os MFs não resolvem o problema de desempenho e, às vezes, até o agravam. Mas ajudam a organizar o desenvolvimento de uma forma mais confortável para uma determinada subequipe, minimizando os problemas acima.
Agora vamos falar sobre a abordagem de combinar MFs em um único aplicativo. Seja qual for a sua escolha, deve parecer um único aplicativo para o usuário. Você pode mesclar tanto no estágio de montagem quanto dinamicamente - durante a execução do código no lado do usuário.
Assim, todas as formas de organizar MFs podem ser atribuídas ao tempo de construção ou ao tempo de execução. Cada um tem os seus prós e contras.
| Tempo de construção | Tempo de execução |
---|---|---|
Verificação de tipo | + | - |
Controle de versão | + | sem sentido |
implantação independente | - | + |
A verificação de tipos desempenha um papel importante no desenvolvimento moderno. Quando é executado por subequipes independentes separadas, torna-se uma necessidade. Como garantir a consistência dos MFs, que eles usem e passem dados com precisão no formato correto, etc.
Ao mesclar microfrentes na hora da construção, você não fica privado da oportunidade de verificar os tipos. No caso de uma união em tempo de execução, você terá que escrever testes de integração para que a frente não “exploda” repentinamente na produção.
O controle de versão e a implantação independente são muito contraditórios:
O controle de versão significa que você pode pegar qualquer versão do MF da outra equipe. Isso é especialmente verdadeiro quando você precisa realizar trabalho adicional para atualizar as dependências do MF-a de outras pessoas. Cada equipe escolhe um melhor momento para atualizar.
A implantação independente dá mais autonomia e independência às equipes. É importante sempre usar as versões mais recentes dos MFs. Isso requer compatibilidade com versões anteriores.
O controle de versão também pode ser implementado com mesclagem de tempo de execução, mas isso não é prático. Faz sentido entrar em contato com o tempo de execução apenas para implantação independente, e o último não pode existir junto com o controle de versão.
A seguir, veremos exemplos de implementações específicas de cada abordagem para combinar MFs.
A maneira mais antiga de organizar MFs. iframe é uma tag especial para passar o endereço de um recurso que será exibido no site principal. O resultado é um site dentro de um site.
Das vantagens , vale destacar a facilidade de implementação, o completo isolamento da lógica e dos estilos, a capacidade de fazer uma implantação independente e a ausência de vinculação a frameworks.
Esses benefícios têm um custo no desempenho, pois cada inserção de um iframe resulta em uma carga de recursos. Você não pode evitar isso: as tentativas de depurar e reanexar um nó DOM não salvarão os recursos carregados anteriormente, você terá que baixá-los novamente. Você pode minimizar a degradação do desempenho configurando o cache.
Para fazer isso, você precisa configurar a invalidação de cache baseada em tempo para recursos imutáveis. Felizmente, todos os cli modernos prontos para uso para os arquivos js e css coletados anexam um pequeno hash ao nome. As desvantagens desse método incluem a incapacidade dos robôs de pesquisa de renderizar iframes para indexação subsequente.
Prós | Contras |
---|---|
fácil implementação | Desempenho |
Isolamento de lógica e estilos | SEO |
implantação independente | |
Estrutura agnóstica | |
A comunidade de front-end espera há muito tempo pela criação de componentes nativos, mas no final eles nunca ganharam a popularidade em massa que muitos esperavam. As três estruturas de front-end mais populares (React, Vue, Angular) ainda criam componentes à sua maneira.
Apesar da capacidade de criar MFs em componentes da web, não vi isso na prática. E isso não é por acaso, existem vários bloqueadores:
As bibliotecas Lit ou Stencil não são populares o suficiente e não são comuns o suficiente. Além disso, não há especialistas suficientes no mercado que saibam trabalhar com eles ou que estejam prontos para aprender.
Os elementos angulares ou o elemento vue-custom permanecem exóticos. Em um ambiente nativo, não há muito sentido em usá-los. Se você já dividiu o aplicativo, então em pacotes npm comuns, para que mais tarde você possa conectar os componentes como quiser. Usar componentes da web com outros frameworks não é razoável porque junto com os componentes gerados, você precisa conectar uma mini-versão do framework no qual eles foram escritos.
Pode ser caro mover peças complexas de funcionalidade para componentes da web. Como você precisará configurar a comunicação do seu componente com o restante do aplicativo, pode não ser justificável remover uma página inteira em um componente personalizado separado.
Os robôs de pesquisa não podem criar um componente da web e isso afetará a otimização de SEO.
Prós | Contras |
---|---|
Adequado para funcionalidade transversal | Difícil de implementar |
Compatível com qualquer estrutura | SEO |
Isolamento de lógica e estilos | |
Desenvolver com pacotes npm tem muitos benefícios. Os desenvolvedores simplesmente importam os componentes e arquivos de que precisam da biblioteca. Ao mesmo tempo, a digitação é preservada no projeto, há controle de versão. A compilação é ideal: o tree-shaking funciona (removendo o código não utilizado durante a compilação) e os desenvolvedores podem facilmente configurar o carregamento lento.
No entanto, este método também tem suas desvantagens. Nesse caso, você será forçado a manter a unidade da pilha, bem como manter as versões de seus MFs e suas dependências transitivas. Por outro lado, isso pode ser uma vantagem: ao publicar uma nova versão do pacote, outras equipes podem assumir a tarefa de aumentar a versão de suas dependências em um momento conveniente para elas, caso precisem introduzir funcionalidades adicionais ou, inversamente, , remova algo.
Prós | Contras |
---|---|
Desempenho | Não possui implantação independente |
SEO | pilha única |
Verificação de tipo | |
Controle de versão | |
Como alternativa ao MF em pacotes npm, considere os submódulos git. Na verdade, são repositórios dentro de um repositório, dentro dos quais também podem existir repositórios. Você pode definir diferentes ramificações em submódulos. Por exemplo, os módulos de recurso podem ter uma ramificação fictícia sem nada. Isso é necessário para que a montagem seja mais rápida e outros módulos não criem efeitos colaterais. Ramificações fictícias podem ser muito úteis para desenvolvimento e testes locais.
Em termos de vantagens e desvantagens, essa abordagem é muito próxima dos pacotes npm. Também apenas importamos algo, mas de uma pasta vizinha, e não de um pacote, e usamos.
Vamos dar uma olhada nas diferenças entre os dois métodos:
Os pacotes NPM são, de fato, um micro produto finito e moderadamente isolado com seus próprios lançamentos e versões. Tudo está focado na criação de funcionalidades reutilizáveis. Mas o aplicativo pode ser complexo/complicado e fedido, então pode ser muito caro empacotar um grande monólito. É aqui que seria razoável considerar os submódulos, porque eles permitem que você corte o repositório de maneira muito grosseira quando movemos a pasta para um repositório separado sem nenhuma preparação adicional.
Os pacotes NPM podem ser aninhados recursivamente. Submódulos também, mas no nível de montagem eles podem começar a duplicar a funcionalidade se um dos submódulos for incluído várias vezes em pastas diferentes como um submódulo separado. Nesse caso, vale a pena usar uma estrutura de módulo mais plana.
Se você precisar implementar rapidamente um recurso em todos os pacotes de uma só vez, o desenvolvimento entre pacotes pode ser extremamente inconveniente. Enquanto tudo permanece igual nos submódulos, você pode fazer edições que afetam outros submódulos. Nesse caso, é fácil depurar. Mas, no final, você não mesclará as próprias alterações - no nível da solicitação de mesclagem, uma equipe terceirizada cujo módulo você tocou pode exigir que você alinhe o código com suas regras.
npm | git submódulos |
---|---|
Reutilização | Corte grosseiro de funcionalidade |
Dependências aninhadas arbitrariamente | Estrutura plana |
Desenvolvimento multiplataforma | Desenvolvimento com qualquer contagem de módulos |
single-spa é essencialmente um framework que combina outros frameworks. Uma tecnologia incrivelmente poderosa, que esconde um grande número de nuances, é um tópico para um artigo separado.
O esquema é semelhante ao iframe, mas o carregamento do MF agora é feito por meio de importação nativa + importmap ou por Systemjs se forem necessários polyfills.
Ao contrário de todos os métodos de organização de MFs, este é altamente adaptado para combinar diferentes estruturas sob si mesmo. Mas vale a pena advertir contra o uso da tecnologia pela própria tecnologia. Se for possível sobreviver com uma pilha, você precisa usá-la. O desenvolvimento pode ficar para sempre sobrecarregado com a manutenção de um projeto tecnicamente complexo e com a correção de quaisquer bugs dos efeitos colaterais de diferentes aplicativos. O usuário pode sentir desconforto, porque. a quantidade de código para download para o cliente será aumentada (os núcleos de diferentes estruturas para diferentes peças de funcionalidade + o núcleo do próprio spa único e seus plug-ins).
Prós | Contras |
---|---|
implantação independente | Documentação extensa, que ainda não cobre todos os casos |
Estrutura agnóstica | Dificuldades com SEO |
CLI poderoso | |
Um plugin webpack 5 que foi desenvolvido especificamente para criar MFs. Tecnologia promissora: um pequeno plug-in de compilação para agrupamento mais correto e importações dinâmicas em tempo de execução.
O esquema quase one-on-one repete spa único, mas agora as importações dinâmicas são usadas para carregar MFs
Prós | Contras |
---|---|
implantação independente | nível baixo |
Fácil realização | |
Compatível com SSR | |
Vejamos o que pode ser aplicado e para quê:
iframe - uma única inserção para uma combinação de incongruentes
componentes da web - quando você precisa de uma pequena funcionalidade de ponta a ponta sem estar vinculado a uma estrutura, como um kit de interface do usuário corporativo
pacotes npm - se houver reutilização entre projetos e/ou você precisar verificar o tipo em tempo de compilação
submódulos git - quando você precisa dividir um projeto e distribuir áreas de responsabilidade
single-spa - quando existe uma forte necessidade de combinar vários quadros indefinidamente, de preferência sem SSR
module-federation - todos os outros cenários para uso de MFs, sujeitos à unidade da pilha.
Cada abordagem é boa à sua maneira e tudo deve ter seu lugar. Antes de mudar para MFs, aconselhamos que você pense se realmente precisa dele. Seja qual for a abordagem escolhida, inevitavelmente complicará algo ao nível do desenvolvimento, CI/CD ou performance. Se houver oportunidades de permanecer em uma única pilha e em um aplicativo monolítico, aceite essa oportunidade com prazer.
E, claro, não se esqueça dos usuários . Em última análise, eles baixam todas as estruturas conectadas e suportam possíveis bugs da integração incorreta de MFs em diferentes partes da funcionalidade. As empresas, por sua vez, terão que pagar pela implementação e suporte de tudo isso.