Micro Frontends Avançados com Angular usando Federação Dinâmica (2023)

Dynamic Module Federation é uma técnica que permite que um aplicativo determine a localização de seus aplicativos remotos em tempo de execução. Ajuda a atingir o caso de uso de“Construa uma vez, implante em qualquer lugar”.

“Criar uma vez, implantar em qualquer lugar” é o conceito de ser capaz de criar um único artefato de compilação de seu aplicativo e implantá-lo em vários ambientes, como preparação e produção.

A dificuldade em conseguir isso com uma Arquitetura Micro Frontend usando Federação de Módulo Estático é que nossos aplicativos remotos terão um local (ou URL) diferente em cada ambiente. Anteriormente, para contabilizar isso, teríamos que especificar o local implantado dos aplicativos remotos e recriar o aplicativo para o ambiente de destino.

Este guia explicará como o conceito de “criar uma vez, implantar em qualquer lugar” pode ser facilmente alcançado em uma arquitetura de microfrontend que usa Federação de módulo dinâmico.

Mirar

O objetivo deste guia é triplo. Queremos ser capazes de:

  • Configurar um Micro Frontend com Federação de Módulo Estático
  • Transforme uma configuração de Federação de Módulo Estático existente para usar a Federação Dinâmica
  • Gere um novo aplicativo Micro Frontend que usa Federação Dinâmica

O que vamos construir

Para atingir os objetivos, faremos o seguinte:

  • Criar uma arquitetura de microfrontend de federação estática
  • Altere o aplicativo Dashboard para usar a Federação Dinâmica
  • Gere um novo aplicativo de painel de funcionários que usará a Federação Dinâmica
    • Ele deve usar o aplicativo de login existente.
    • Ele deve usar um novo aplicativo Todo.

Primeiros passos

Criar um espaço de trabalho Nx

Para começar, precisamos criar um novo Nx Workspace. Podemos fazer isso facilmente com:

#npmnpx criar-nx-espaço de trabalho ng-mf

#Fioyarn criar nx-workspace ng-mf --packageManager=yarn

Você será solicitado a fornecer uma predefinição. Selecione"Monorepo Integrado"então"Angular".

Criando nossos aplicativos

Precisamos gerar dois aplicativos que suportem a Federação de Módulos.

Começaremos com o aplicativo Admin Dashboard, que funcionará como um aplicativo host para os Micro-Frontends (MFEs):

#npmnpx nx g @nrwl/angular: painel do host

#Fiofio nx g @nrwl/angular: painel do host

O gerador de aplicativos criará e modificará os arquivos necessários para configurar o aplicativo Angular.

Agora, vamos gerar o aplicativo Login como um aplicativo remoto.

#npmnpx nx g @nrwl/angular:login remoto --host=painel

#Fioyarn nx g @nrwl/angular:login remoto --host=painel

--hospedar

nós fornecemos--host=painelcomo uma opção. Isso informa ao gerador que esse aplicativo remoto será consumido pelo aplicativo Dashboard. O gerador vinculará automaticamente esses dois aplicativos nomodule-federation.config.jsque é usado nowebpack.config.js._

Mais detalhes

ORemoteEntryModulegerado será importado emapp.module.tsarquivo, no entanto, ele não é usado noAppModuleem si. Isso permite que o TS encontre o módulo durante a compilação, permitindo que ele seja incluído no pacote criado. Isso é necessário para que o Plug-in de Federação do Módulo exponha o Módulo corretamente. Você pode optar por importar oRemoteEntryModulenoAppModulese desejar, porém, não é necessário._

O que foi gerado?

Vamos dar uma olhada depois de gerar cada aplicativo.

Para ambas as aplicações, o gerador fez o seguinte:

  • Criou os arquivos padrão do aplicativo Angular
  • Adicionado ummodule-federation.config.jsarquivo
  • Adicionado umwebpack.config.jsewebpack.prod.config.js
  • Adicionado umbootstrap.tsarquivo
  • Movido o código que normalmente está emmain.tsparabootstrap.ts
  • Mudadomain.tsimportar dinamicamentebootstrap.ts (isso é necessário para a Federação de Módulos corrigir versões de carregamento de bibliotecas compartilhadas)
  • Atualizado oconstruiralvo noprojeto.jsonusar o@nrwl/angular:webpack-browserexecutor(isso é necessário, pois suporta a passagem de uma configuração personalizada do Webpack para o compilador Angular)
  • Atualizado oserviralvo para usar@nrwl/angular:webpack-dev-server (isso é necessário, pois primeiro precisamos do Webpack para criar o aplicativo com nossa configuração personalizada do Webpack)

As principais diferenças residem na configuração do plug-in Module Federation dentro de cada aplicativomodule-federation.config.js.

Vemos o seguinte na configuração do micro frontend do Login:

módulo.exportações = { nome:'Conecte-se', expõe: { './Módulo':'apps/login/src/app/remote-entry/entry.module.ts',},};

Dando uma olhada em cada propriedade da configuração por sua vez:

  • nomeé o nome que o Webpack atribui ao aplicativo remoto. Istodevecorresponda ao nome do aplicativo.
  • expõeé a lista de arquivos de origem que o aplicativo remoto fornece aplicativos de shell de consumo para seu próprio uso.

Esta configuração é então usada nowebpack.config.jsarquivo:

const{ withModuleFederation } =exigir('@nrwl/angular/module-federation');constconfiguração =exigir('./module-federation.config');módulo.exports = withModuleFederation(config);

Podemos ver o seguinte na configuração do micro frontend do Dashboard:

módulo.exportações = { nome:'painel', Remotos: ['Conecte-se'],};

A principal diferença a observar com a configuração do Dashboard é aRemotosvariedade. É aqui que você lista os aplicativos remotos que deseja consumir em seu aplicativo host.

Você dá a ele um nome que pode referenciar em seu código, neste casoConecte-se. Nx encontrará onde é servido.

Agora que temos nossos aplicativos gerados, vamos construir algumas funcionalidades para cada um.

Adicionando funcionalidade

Começaremos construindo o aplicativo Login, que consistirá em um formulário de login e alguma lógica de autorização muito básica e insegura.

Biblioteca do usuário

Vamos criar uma biblioteca de acesso a dados do usuário que será compartilhada entre o aplicativo host e o aplicativo remoto. Isso será usado para determinar se há um usuário autenticado, além de fornecer lógica para autenticar o usuário.

nx g @nrwl/angular:lib shared/data-access-user

Isso criará uma nova biblioteca para usarmos.

Precisamos de um Angular Service que usaremos para manter o estado:

nx g @nrwl/angular:service user --project=shared-data-access-user

Isso criará um arquivousuário.serviço.tsdebaixo deusuário-acesso-compartilhado/dadosbiblioteca. Altere seu conteúdo para corresponder:

shared/data-access-user/user.service.ts

importar{ Injetável }de '@angular/núcleo';importar{ComportamentoAssunto}de 'rxjs';@Injetável({ Fornecido em:'raiz',})exportar aula UserService { privadoisUserLoggedIn =novoComportamentoAssunto(falso);isUserLoggedIn$ =esse.isUserLoggedIn.asObservable(); verificarCredenciais(nome de usuário:corda, senha:corda){ se(nome de usuário ==='demonstração'&& senha ==='demonstração') { esse.isUserLoggedIn.next(verdadeiro);} } sair(){ esse.isUserLoggedIn.next(falso);}}

Adicione uma nova exportação ao usuário compartilhado/de acesso a dadosindex.tsarquivo:
exportar * de './lib/user.service';

Aplicativo de login

Primeiro, adicioneMódulo Formspara oimportamatriz em seuentrada remota/entry.module.tsarquivo:

entrada remota/entry.module.ts

importar{ NgModule }de '@angular/núcleo';importar{ CommonModule }de '@angular/comum';importar{ RouterModule }de '@angular/roteador';importar{ RemoteEntryComponent }de './entrada.componente';importar{ NxWelcomeComponent }de './nx-welcome.component';importar{ remoteRoutes }de './entrada.rotas';importar{ Módulo Forms }de '@angular/formas';@NgModule({ declarações: [RemoteEntryComponent, NxWelcomeComponent], importa: [CommonModule, FormsModule, RouterModule.forChild(remoteRoutes)], provedores: [],})exportar aula RemoteEntryModule {}

Em seguida, queremos configurar nossoentrada.componente.tsarquivo para que ele renderize um login e tenha injetado nossoUserServicepara permitir o login do usuário:

entrada.componente.ts

importar{Componente}de '@angular/núcleo';importar{ Serviço de usuário }de '@ng-mf/shared/data-access-user';@Componente({ seletor:'ng-mf-login-entry', modelo:`

`, estilos: [ `.login-app {largura: 30vw;borda: 2px preto tracejado;preenchimento: 8px;margem: 0 automático;}.forma de login {exibição: flexível;itens de alinhamento: centro;flex-direction: coluna;margem: 0 automático;preenchimento: 8px;}rótulo {exibição: bloco;}`,],})exportar aula RemoteEntryComponent {nome de usuário ='';senha ='';isLoggedIn$ =esse.userService.isUserLoggedIn$; construtor(privadouserService: UserService){} Conecte-se(){ esse.userService.checkCredentials(esse.nome de usuário,esse.senha);}}

Mais detalhes

Isso pode ser melhorado com o tratamento de erros, etc., mas para os propósitos deste tutorial, vamos mantê-lo simples.

Vamos adicionar uma rota ao nosso aplicativo Login para que possamos renderizar oRemoteEntryComponent.
Abrirapp.module.tse adicione a seguinte rota aoRouterMoodle.forRoot(...)declaração.

app.module.ts

RouterModule.forRoot([{ caminho:'', loadChildren:() => importar('./entrada-remota/entrada.module').então((m) =>m.RemoteEntryModule),}, ],{navegação inicial:'bloqueio ativado'});

Agora vamos servir o aplicativo e visualizá-lo em um navegador para verificar se o formulário é renderizado corretamente.

nx run login:serve

Podemos ver se navegamos em um navegador parahttp://localhost:4201que vemos o formulário de login renderizado.

Se digitarmos o nome de usuário e a senha corretos(demonstração, demonstração), também podemos ver que o usuário é autenticado!

Perfeito! Nosso aplicativo de login está completo.

Aplicativo de painel

Agora vamos criar a aplicação Dashboard onde vamos esconder algum conteúdo caso o usuário não esteja autenticado. Se o usuário não estiver autenticado, apresentaremos a ele o aplicativo de login onde ele poderá fazer login.

Para que isso funcione, o estado dentroUserServicedevem ser compartilhados entre os dois aplicativos. Normalmente, com o Module Federation no Webpack, você deve especificar os pacotes a serem compartilhados entre todos os aplicativos da sua solução Micro Frontend.
No entanto, aproveitando o gráfico de projeto do Nx, o Nx encontrará e compartilhará automaticamente as dependências de seus aplicativos.

Mais detalhes

Isso ajuda a impor uma política de versão única e reduz o risco deAnarquia de microfrontend_

Agora, vamos deletar oapp.component.htmleapp.component.cssarquivos no aplicativo Dashboard. Eles não serão necessários para este tutorial.

Por fim, vamos adicionar nossa lógica aapp.component.ts. Altere-o para corresponder ao seguinte:

app.component.ts

importar{ Componente, OnInit }de '@angular/núcleo';importar{ Roteador }de '@angular/roteador';importar{ distintiveUntilChanged }de 'rxjs/operadores';importar{ Serviço de usuário }de '@ng-mf/shared/data-access-user';@Componente({ seletor:'ng-mf-root', modelo:`

Painel do administrador
Você está autenticado para poder ver este conteúdo.
`,})exportar aula AppComponent implementos OnInit {isLoggedIn$ =esse.userService.isUserLoggedIn$; construtor(privadouserService: UserService,privadoroteador: roteador){} com OnInit(){ esse.isLoggedIn$.pipe(distinctUntilChanged()).se inscrever(assíncrono (loggedIn) => { // Coloca a navegação na fila após o bloqueio de initialNavigation ser concluído setTimeout(() =>{ se (!loggedIn) { esse.router.navigateByUrl('Conecte-se');}outro{ esse.router.navigateByUrl('');} }); }); }}

Podemos executar o aplicativo de painel e o aplicativo de login e você pode experimentá-lo usando:

painel de serviço nx --devRemotes=login

Isso conclui a configuração necessária para uma abordagem de Micro Frontend usando Federação de Módulo Estático.

Ao servir aplicativos de federação de módulo no modo dev localmente, haverá uma saída de erro para o console,import.meta não pode ser usado fora de um módulo, e o script que está vindo éestilos.js. É uma saída de erro conhecida, mas na verdade não causa nenhuma quebra, tanto quanto nossos testes mostraram. É porque o compilador Angular anexa o arquivo styles.js ao index.html em uma tag de script com defer.

Ele precisa ser anexado comtipo=módulo, mas o Angular não pode fazer essa alteração porque quebra o HMR. Eles também não fornecem nenhuma maneira de se conectar a esse processo para que possamos corrigi-lo nós mesmos.

A boa notícia é que o erro não se propaga para a produção, porque os estilos são compilados em um arquivo CSS, então não há JS errado para registrar um erro.

Vale ressaltar que não houve erros reais ou quebras observadas em nossos testes.

Convertendo o aplicativo Dashboard

O aplicativo Dashboard é um aplicativo host que carrega os aplicativos remotos em tempo de execução com base em seus locais implantados quando o aplicativo foi criado usando a federação de módulo estático.

Queremos mudar isso para que o aplicativo Dashboard possa fazer uma solicitação de rede em tempo de execução para descobrir os locais implantados dos aplicativos remotos.

Existem 3 etapas envolvidas com isso:

  • Faça uma solicitação de rede para buscar os locais dos aplicativos remotos(as Definições Remotas).
  • Defina as Definições remotas para que o webpack saiba como encontrar os aplicativos remotos quando solicitado.
  • Altere a forma como carregamos os aplicativos remotos para que o webpack possa buscar todos os arquivos necessários corretamente.

Buscar e definir as definições remotas

Talvez um dos métodos mais fáceis de buscar as Definições Remotas em tempo de execução seja armazená-las em um arquivo JSON que pode estar presente em cada ambiente. O aplicativo Host só precisa fazer uma solicitação GET para o arquivo JSON.

Vamos começar criando este arquivo. Adicionar ummodule-federation.manifest.jsonarquivo para osrc/assets/pasta em nosso aplicativo Dashboard com o seguinte conteúdo:

{ "Conecte-se":"http://localhost:4201"}

Em seguida, abramain.tsdebaixo deorigem/pasta e substitua-a pelo seguinte:

src/main.ts

importar{ setRemoteDefinitions }de '@nrwl/angular/mf';buscar('/assets/module-federation.manifest.json').então((res) =>res.json()).então((definições) =>setRemoteDefinitions(definições)).então(() => importar('./bootstrap').pegar((errar) => console.erro(erro)));

Você notará que nós:

  • Buscar o arquivo JSON
  • ChamardefinirRemoteDefinitionscom o conteúdo do arquivo JSON
    • Isso informa ao webpack onde cada um de nossos aplicativos remotos foi implantado!

Alterar como os Remotes são carregados

No momento, o webpack está construindo estaticamente nosso aplicativo, informando no momento da construção onde estão nossos Remotes. Isso porque eles são especificados nomodule-federation.config.jsarquivo.

Abra omodule-federation.config.jsarquivo na raiz do nossoaplicativos/painel/pasta e defina oRemotospropriedade seja uma matriz vazia. Deve ficar assim:

módulo.exportações = { nome:'painel', Remotos: [],};

Em seguida, precisamos alterar como nosso aplicativo tenta carregar o Remote quando ele é roteado. Abrirapp.module.tsdebaixo desrc/aplicativo/pasta.

Você deve ver a seguinte linha noRouterModule.forRoot():

{ caminho:'Conecte-se', loadChildren:() => importar('login/Módulo').então((m) =>m.RemoteEntryModule),}

Substitua-o pelo seguinte:

{ caminho:'Conecte-se', loadChildren:() =>loadRemoteModule('Conecte-se','./Módulo').então( (m) =>m.RemoteEntryModule),}

Você também precisará adicionar a seguinte importação ao topo do arquivo:

importar{ loadRemoteModule }de '@nrwl/angular/mf';

OloadRemoteModuleO método auxiliar simplesmente oculta alguma lógica que verificará se o aplicativo remoto foi carregado e, se não, carregá-lo e, em seguida, solicitar o módulo exposto correto dele.

Resumo

Essas são todas as mudanças necessárias para substituir a Federação de Módulo Estático pela Federação de Módulo Dinâmico.

Correndo:

painel de serviço nx --devRemotes=login

Deve resultar no mesmo comportamento de antes, exceto que nosso aplicativo Dashboard está esperando até o tempo de execução para descobrir o local implantado de nosso aplicativo Login.

Repositório de exemplo/Coly010/nx-ng-dyn-th

Na próxima seção, veremos como os geradores de Nx podem ser usados ​​para automatizar muito desse processo para nós!

Criando um novo aplicativo de host dinâmico

O Nx fornece geradores que visam agilizar o processo de configuração de uma arquitetura Dynamic Micro Frontend.

Para mostrar isso, vamos criar um novo aplicativo Host que usará nosso aplicativo Login anterior, bem como um novo aplicativo Todo Remote.

Gerar o aplicativo Host do Funcionário

Execute o seguinte comando para gerar um novo aplicativo Host pré-configurado para Federação Dinâmica e adicione especificar o aplicativo Login Remote que deseja adicionar:

nx g @nrwl/angular:funcionário host --remotes=login --dynamic

Isso vai gerar:

  • aplicação angular
  • Configuração do Webpack(webpack.config.js)
  • Configuração da Federação do Módulo(module-federation.config.js)
  • Ficheiro de Manifesto Micro Frontend(module-federation.manifest.json)
  • Alterações no bootstrap do aplicativo para buscar o Manifesto do Micro Frontend, definir as Definições Remotas e carregar o aplicativo de Login corretamente

Você deve dar uma olhada nos arquivos gerados e ver como o aplicativo Login Remote foi adicionado aomodule-federation.manifest.jsonarquivo e as pequenas alterações emmain.tseapp.module.tspara carregar os Remotes dinamicamente.

Gere o aplicativo Todo Remote

Vamos demonstrar como ao especificar um host dinâmico ao adicionar um novo aplicativo remoto, o aplicativo remoto será adicionado ao arquivo de manifesto do micro frontend do host corretamente.

nx g @nrwl/angular:remote todo --host=funcionário

Você notará que isso gerará a mesma saída que o aplicativo Login Remote no guia anterior. Há uma diferença.

Como o aplicativo Host está usando Federação Dinâmica, o novo Remote será adicionado ao Hostmodule-federation.manifest.json.

Resumo

A Federação Dinâmica é a maneira perfeita de resolver o problema de“Construa uma vez, implante em qualquer lugar”. Ele pode ser usado em conjunto com soluções de CD que envolvem a criação de ambientes para diferentes estágios do processo de lançamento sem a necessidade de recriar o aplicativo. O pipeline de CD só precisa substituir um arquivo JSON nesse ambiente por um que contenha os valores corretos para os locais implantados dos aplicativos remotos.

Top Articles
Latest Posts
Article information

Author: Horacio Brakus JD

Last Updated: 03/08/2023

Views: 5327

Rating: 4 / 5 (51 voted)

Reviews: 82% of readers found this page helpful

Author information

Name: Horacio Brakus JD

Birthday: 1999-08-21

Address: Apt. 524 43384 Minnie Prairie, South Edda, MA 62804

Phone: +5931039998219

Job: Sales Strategist

Hobby: Sculling, Kitesurfing, Orienteering, Painting, Computer programming, Creative writing, Scuba diving

Introduction: My name is Horacio Brakus JD, I am a lively, splendid, jolly, vivacious, vast, cheerful, agreeable person who loves writing and wants to share my knowledge and understanding with you.