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 create-nx-workspace ng-mfe
# Fioyarn criar nx-workspace ng-mfe --packageManager=yarn
Você será solicitado a fornecer uma predefinição. Recomendamos selecionarvazio
pois permitirá um controle mais preciso sobre a configuração do seu espaço de trabalho.
Você também será perguntado se deseja configurar o Nx Cloud. Para este tutorial, selecioneNão
, no entanto, eu recomendo que você leia mais sobre issoaqui.
Adicione o plug-in Angular
Para adicionar recursos relacionados ao Angular ao nosso monorepo recém-criado, precisamos instalar o plug-in Angular. Novamente, isso é muito fácil de fazer:
OBSERVAÇÃO:Verifique se agora você está na raiz do seu monorepo no seu terminal. Se não, corracd de mfe
#Npmnpm install --save-dev @nrwl/angular
# Fioyarn add -D @nrwl/angular
Simples! Agora você pode usar Nx Generators para criar aplicações e bibliotecas 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
Observação:nós fornecemos--host=painel
como 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.js
que é usado nowebpack.config.js
.
Observação: ORemoteEntryModule
gerado será importado emapp.module.ts
arquivo, no entanto, ele não é usado noAppModule
em 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 oRemoteEntryModule
noAppModule
se desejar, no entanto, 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 um
module-federation.config.js
arquivo - Adicionado um
webpack.config.js
ewebpack.prod.config.js
- Adicionado um
bootstrap.ts
arquivo - Movido o código que normalmente está em
main.ts
parabootstrap.ts
- Mudado
main.ts
importar dinamicamentebootstrap.ts
(isso é necessário para a Federação de Módulos corrigir versões de carregamento de bibliotecas compartilhadas) - Atualizado o
construir
alvo noprojeto.json
usar o@nrwl/angular:webpack-browser
executor(isso é necessário, pois suporta a passagem de uma configuração personalizada do Webpack para o compilador Angular) - Atualizado o
servir
alvo para usar@nrwl/angular:webpack-servidor
(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:
1módulo.exportações = {2 nome:'Conecte-se',3 expõe: {4 './Módulo':'apps/login/src/app/remote-entry/entry.module.ts',5},6};
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.js
arquivo:
1const{ withModuleFederation } =exigir('@nrwl/angular/module-federation');2constconfiguração =exigir('./module-federation.config');3módulo.exports = withModuleFederation(config);
Podemos ver o seguinte na configuração do micro frontend do Dashboard:
1módulo.exportações = {2 nome:'painel',3 Remotos: ['Conecte-se'],4};
A principal diferença a observar com a configuração do Dashboard é aRemotos
variedade. É 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.ts
debaixo deusuário-acesso-compartilhado/dados
biblioteca. Altere seu conteúdo para corresponder:
1importar{ Injetável }de '@angular/núcleo';2importar{ComportamentoAssunto}de 'rxjs';3@Injetável({4 Fornecido em:'raiz',5})6exportar aula UserService {7 privadoisUserLoggedIn =novoComportamentoAssunto(falso);8isUserLoggedIn$ =esse.isUserLoggedIn.asObservable();9 verificarCredenciais(nome de usuário:corda, senha:corda){10 se(nome de usuário ==='demonstração'&& senha ==='demonstração') {11 esse.isUserLoggedIn.next(verdadeiro);12}13}14 sair(){15 esse.isUserLoggedIn.next(falso);16}17}
Adicione uma nova exportação ao usuário compartilhado/de acesso a dadosindex.ts
arquivo:exportar * de './lib/user.service';
Aplicativo de login
Primeiro, adicioneMódulo Forms
para oimporta
matriz em seuentrada remota/entry.module.ts
arquivo:
1importar{ NgModule }de '@angular/núcleo';2importar{ Módulo Forms }de '@angular/formas';3importar{ CommonModule }de '@angular/comum';4importar{ RouterModule }de '@angular/roteador';5importar{ RemoteEntryComponent }de './entrada.componente';6@NgModule({7 declarações: [RemoteEntryComponent],8 importa: [9CommonModule,10FormsMódulo,11RouterModule.forChild([12{13 caminho:'',14 componente: RemoteEntryComponent,15},16]),17],18 provedores: [],19})20exportar aula RemoteEntryModule {}
Em seguida, queremos configurar nossoentrada.componente.ts
arquivo para que ele renderize um login e tenha injetado nossoUserService
para permitir o login do usuário:
1importar{Componente}de '@angular/núcleo';2importar{ Serviço de usuário }de '@ng-mfe/shared/data-access-user';3@Componente({4 seletor:'ng-mfe-login-entry',5 modelo:`678 9Nome de usuário:101112 13Senha:1415161718O usuário está logado!1920`,21 estilos: [22 `23.login-app {24largura: 30vw;25borda: 2px preto tracejado;26preenchimento: 8px;27margem: 0 automático;28}29.forma de login {30exibição: flexível;31itens de alinhamento: centro;32flex-direction: coluna;33margem: 0 automático;34preenchimento: 8px;35}36rótulo {37exibição: bloco;38}39`,40],41})42exportar aula RemoteEntryComponent {43nome de usuário ='';44senha ='';45isLoggedIn$ =esse.userService.isUserLoggedIn$;46 construtor(privadouserService: UserService){}47 Conecte-se(){48 esse.userService.checkCredentials(esse.nome de usuário,esse.senha);49}50}
Observação: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.ts
e adicione a seguinte rota aoRouterMoodle.forRoot(...)
declaração.
1RouterModule.forRoot(2[3{4 caminho:'',5 loadChildren:() =>6 importar('./entrada-remota/entrada.module').então((m) =>m.RemoteEntryModule),7},8],9{navegação inicial:'bloqueio ativado'}10);
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:4201
que 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 dentroUserService
devem 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.
Observação:Isso ajuda a impor uma política de versão única e reduz o risco deAnarquia de microfrontend
Agora, vamos deletar oapp.component.html
eapp.component.css
arquivos 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:
1importar{ Componente, OnInit }de '@angular/núcleo';2importar{ Roteador }de '@angular/roteador';3importar{ distintiveUntilChanged }de 'rxjs/operadores';4importar{ Serviço de usuário }de '@ng-mfe/shared/data-access-user';5@Componente({6 seletor:'ng-mfe-root',7 modelo:`8Painel do administrador910Você está autenticado para poder ver este conteúdo.1112 13`,14})15exportar aula AppComponent implementos OnInit {16isLoggedIn$ =esse.userService.isUserLoggedIn$;17 construtor(privadouserService: UserService,privadoroteador: roteador){}18 com OnInit(){19 esse.isLoggedIn$20.pipe(distinctUntilChanged())21.se inscrever(assíncrono (loggedIn) => {22 se (!loggedIn) {23 esse.router.navigateByUrl('Conecte-se');24}outro{25 esse.router.navigateByUrl('');26}27});28}29}
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.
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.json
arquivo para osrc/assets/
pasta em nosso aplicativo Dashboard com o seguinte conteúdo:
1{2 "Conecte-se":"http://localhost:4201"3}
Em seguida, abramain.ts
debaixo deorigem/
pasta e substitua-a pelo seguinte:
1importar{ setRemoteDefinitions }de '@nrwl/angular/simples';23buscar('/assets/module-federation.manifest.json')4.então((res) =>res.json())5.então((definições) =>setRemoteDefinitions(definições))6.então(() => importar('./bootstrap').pegar((errar) => console.erro(erro)));
Você notará que nós:
- Buscar o arquivo JSON
- Chamar
definirRemoteDefinitions
com 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.js
arquivo.
Abra omodule-federation.config.js
arquivo na raiz do nossoaplicativos/painel/
pasta e defina oRemotos
propriedade seja uma matriz vazia. Deve ficar assim:
1módulo.exportações = {2 nome:'painel',3 Remotos: [],4};
Em seguida, precisamos alterar como nosso aplicativo tenta carregar o Remote quando ele é roteado. Abrirapp.module.ts
debaixo desrc/aplicativo/
pasta.
Você deve ver a seguinte linha noRouterModule.forRoot()
:
1{2 caminho:'Conecte-se',3 loadChildren:() =>4 importar('login/Módulo').então((m) =>m.RemoteEntryModule),5}
Substitua-o pelo seguinte:
1{2 caminho:'Conecte-se',3 loadChildren:() =>4loadRemoteModule('Conecte-se','./Módulo').então(5 (m) =>m.RemoteEntryModule6),7}
Você também precisará adicionar a seguinte importação ao topo do arquivo:
1importar{ loadRemoteModule }de '@nrwl/angular/simples';
OloadRemoteModule
O 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.
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.json
arquivo e as pequenas alterações emmain.ts
eapp.module.ts
para 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.