Neste artigo vamos atualizar o conceito apresentado no artigo original "Desacoplando frontend com module-federation Angular - Parte 1", adaptando o projeto para o Angular 21 e para a arquitetura moderna de Micro Frontends.
Nota: no Angular moderno, a abordagem recomendada para manter o conceito de Module Federation sem depender do Webpack é utilizar o Native Federation. Ele utiliza ES Modules, Import Maps e integra-se ao Application Builder baseado em esbuild do Angular.
O objetivo continua sendo o mesmo:
- Criar uma aplicação Shell/Host;
- Criar aplicações Remote/Micro Frontends;
- Permitir que o Shell carregue os Micro Frontends em runtime;
- Manter cada aplicação com seu próprio código e deploy;
- Reduzir o acoplamento entre as aplicações.
O que são Micro Frontends?
O conceito de Micro Frontends é semelhante ao conceito de Microservices.
Em vez de termos uma única aplicação frontend grande, podemos dividir a aplicação em partes menores, cada uma responsável por uma determinada área funcional.
Por exemplo:
┌─────────────────────┐
│ SHELL │
│ Angular 21 │
└──────────┬──────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ MFE 1 │ │ MFE 2 │ │ MFE 3 │
│ Crédito │ │ Conta │ │ Growth │
└───────────┘ └───────────┘ └───────────┘
Cada Micro Frontend pode possuir:
- seu próprio código;
- seu próprio pipeline;
- seu próprio deploy;
- sua própria equipe;
- seu próprio ciclo de desenvolvimento.
Para o usuário final, entretanto, tudo pode aparecer como uma única aplicação.
O que mudou desde o Angular utilizado no artigo original?
No artigo original utilizamos:
Angular
+
Webpack
+
Module Federation
+
AppModule
+
AppRoutingModule
No Angular 21, temos uma arquitetura mais moderna:
Angular 21
+
Standalone Components
+
Application Builder / esbuild
+
Native Federation
+
ES Modules
+
Import Maps
O Native Federation mantém a mesma ideia conceitual do Module Federation, mas não depende do Webpack. A implementação utiliza padrões nativos do navegador, como ES Modules e Import Maps.
Ferramentas necessárias
Para acompanhar este exemplo, recomendo:
- Node.js
- Angular CLI
- VS Code
- npm
Verifique as versões:
node -v
npm -v
ng version
Neste exemplo vamos utilizar:
Angular 21
Angular CLI 21
TypeScript compatível com Angular 21
1. Criando o workspace
Vamos criar uma pasta para nosso exemplo:
mkdir artigo-mfe
cd artigo-mfe
Agora vamos criar nossa aplicação Shell:
ng new mfe-host
Durante a criação, podemos utilizar:
Routing: Yes
Stylesheet: CSS
Entre no projeto:
cd mfe-host
Abra o projeto no VS Code:
code .
2. Criando os Micro Frontends
Vamos voltar para a pasta principal:
cd ..
Agora criaremos dois Micro Frontends:
ng new mfe-1
e:
ng new mfe-2
Ao final teremos:
artigo-mfe/
│
├── mfe-host/
│
├── mfe-1/
│
└── mfe-2/
Nosso objetivo será:
mfe-host
│
├── /mfe-1 → mfe-1
│
└── /mfe-2 → mfe-2
3. Configurando o MFE-1
Entre no projeto:
cd mfe-1
Agora vamos adicionar o Native Federation:
ng add @angular-architects/native-federation --project mfe-1 --port 4201 --type remote
O ng add configura automaticamente a aplicação para atuar como um Remote.
O pacote fornece integração com o Angular CLI e com o Application Builder baseado em esbuild.
4. Configurando o MFE-2
Volte para a raiz:
cd ..
Entre no segundo Micro Frontend:
cd mfe-2
Execute:
ng add @angular-architects/native-federation --project mfe-2 --port 4202 --type remote
Agora temos:
mfe-1 → http://localhost:4201
mfe-2 → http://localhost:4202
5. Configurando o Shell
Volte para a raiz:
cd ..
Entre no Shell:
cd mfe-host
Agora configuraremos o projeto como um Dynamic Host:
ng add @angular-architects/native-federation --project mfe-host --port 4200 --type dynamic-host
O dynamic-host é importante porque permite que os Micro Frontends sejam definidos através de um manifesto.
Essa abordagem também permite alterar os endpoints dos Micro Frontends durante o deploy sem precisar recompilar o Shell.
6. Estrutura do projeto
Depois da configuração teremos aproximadamente:
mfe-host/
│
├── src/
│ ├── app/
│ ├── assets/
│ │ └── federation.manifest.json
│ │
│ ├── main.ts
│ └── bootstrap.ts
│
├── federation.config.js
└── package.json
A configuração de federation ficará separada da configuração normal do Angular.
7. Configurando o MFE-1
No projeto mfe-1, teremos um arquivo:
federation.config.js
Uma configuração semelhante a:
const {
withNativeFederation,
shareAll,
} = require('@angular-architects/native-federation/config');
module.exports = withNativeFederation({
name: 'mfe-1',
exposes: {
'./Component': './src/app/app.component.ts',
},
shared: {
...shareAll({
singleton: true,
strictVersion: true,
requiredVersion: 'auto',
}),
},
skip: [
'rxjs/ajax',
'rxjs/fetch',
'rxjs/testing',
'rxjs/webSocket',
],
});
O ponto mais importante aqui é:
exposes: {
'./Component': './src/app/app.component.ts',
}
Estamos dizendo:
Quando outro aplicativo solicitar
./Component, entregue oAppComponentdeste Micro Frontend.
O host não precisa conhecer o caminho físico do arquivo.
Ele conhece apenas:
./Component
Essa é uma das ideias fundamentais da Federation.
8. Configurando o MFE-2
No projeto mfe-2, faça a mesma configuração:
const {
withNativeFederation,
shareAll,
} = require('@angular-architects/native-federation/config');
module.exports = withNativeFederation({
name: 'mfe-2',
exposes: {
'./Component': './src/app/app.component.ts',
},
shared: {
...shareAll({
singleton: true,
strictVersion: true,
requiredVersion: 'auto',
}),
},
skip: [
'rxjs/ajax',
'rxjs/fetch',
'rxjs/testing',
'rxjs/webSocket',
],
});
Agora temos dois Remotes:
mfe-1
└── ./Component
mfe-2
└── ./Component
9. Configurando o Manifest
Agora vamos para o projeto:
mfe-host
Procure:
src/assets/federation.manifest.json
Configure:
{
"mfe-1": "http://localhost:4201/remoteEntry.json",
"mfe-2": "http://localhost:4202/remoteEntry.json"
}
O Manifest funciona como um mapa:
mfe-1 → http://localhost:4201/remoteEntry.json
mfe-2 → http://localhost:4202/remoteEntry.json
Uma diferença importante em relação ao artigo original é que agora não trabalhamos diretamente com:
remoteEntry.js
como acontecia no Module Federation baseado em Webpack.
O Native Federation utiliza metadados e módulos ES, sendo o remoteEntry.json utilizado pelo mecanismo para descobrir os recursos publicados pelo Remote.
10. Como o Manifest funciona?
O fluxo é aproximadamente:
Browser
│
▼
federation
manifest
│
┌──────────┴──────────┐
│ │
▼ ▼
mfe-1 mfe-2
│ │
▼ ▼
remoteEntry.json remoteEntry.json
│ │
▼ ▼
Component Component
O Shell não precisa conhecer previamente todo o código dos Micro Frontends.
Ele conhece apenas:
nome do remote
+
endereço do remote
+
nome do recurso exposto
11. Inicialização do Federation
O ng add também configura o processo de inicialização.
O main.ts terá uma estrutura semelhante a:
import { initFederation } from '@angular-architects/native-federation';
initFederation('/assets/federation.manifest.json')
.catch((err) => console.error(err))
.then(() => import('./bootstrap'))
.catch((err) => console.error(err));
O processo é:
main.ts
│
▼
initFederation()
│
▼
federation.manifest.json
│
├── mfe-1
│
└── mfe-2
│
▼
bootstrap.ts
│
▼
Angular Application
A inicialização do Federation acontece antes do bootstrap da aplicação Angular.
12. Configurando as rotas do Shell
Agora vamos configurar o carregamento dos Micro Frontends.
No Angular 21 podemos utilizar Standalone APIs.
Crie ou altere:
src/app/app.routes.ts
Utilize:
import { Routes } from '@angular/router';
import { loadRemoteModule } from '@angular-architects/native-federation';
export const routes: Routes = [
{
path: 'mfe-1',
loadComponent: () =>
loadRemoteModule('mfe-1', './Component')
.then((m) => m.AppComponent),
},
{
path: 'mfe-2',
loadComponent: () =>
loadRemoteModule('mfe-2', './Component')
.then((m) => m.AppComponent),
},
];
Observe a diferença em relação ao artigo original.
Antes tínhamos:
loadChildren()
e:
AppModule
Agora podemos trabalhar diretamente com:
loadComponent()
e:
AppComponent
Isso acontece porque o Angular moderno utiliza Standalone Components como abordagem principal.
O Native Federation suporta esse modelo diretamente.
13. Configurando o AppComponent do Shell
No:
mfe-host/src/app/app.component.html
podemos utilizar:
<div>
<h1>Angular 21 - Micro Frontends</h1>
<nav>
<a routerLink="/mfe-1">
Acesse MFE-1
</a>
<br />
<a routerLink="/mfe-2">
Acesse MFE-2
</a>
</nav>
</div>
<hr />
<router-outlet></router-outlet>
Agora teremos:
Angular 21 - Micro Frontends
Acesse MFE-1
Acesse MFE-2
14. Configurando o MFE-1
Vamos alterar o componente do mfe-1.
Por exemplo:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
template: `
<div>
<h2>MFE-1</h2>
<p>
Este conteúdo foi carregado remotamente.
</p>
</div>
`,
})
export class AppComponent {
}
15. Configurando o MFE-2
No mfe-2:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
template: `
<div>
<h2>MFE-2</h2>
<p>
Este conteúdo também foi carregado remotamente.
</p>
</div>
`,
})
export class AppComponent {
}
16. Subindo os projetos
Agora precisamos executar os três projetos.
Terminal 1 — MFE-1
cd mfe-1
ng serve
Aplicação:
http://localhost:4201
Terminal 2 — MFE-2
cd mfe-2
ng serve
Aplicação:
http://localhost:4202
Terminal 3 — Shell
cd mfe-host
ng serve
Aplicação:
http://localhost:4200
17. Testando
Abra:
http://localhost:4200
Clique:
Acesse MFE-1
O Shell irá executar:
loadRemoteModule('mfe-1', './Component')
O Native Federation irá localizar:
mfe-1
no:
federation.manifest.json
e descobrir:
http://localhost:4201/remoteEntry.json
Depois carregará:
./Component
que aponta para:
mfe-1/src/app/app.component.ts
O resultado será:
┌──────────────────────────────────┐
│ MFE-HOST │
│ │
│ Acesse MFE-1 │
│ Acesse MFE-2 │
│ │
│ ┌────────────────────────────┐ │
│ │ MFE-1 │ │
│ │ │ │
│ │ Conteúdo carregado │ │
│ │ remotamente │ │
│ └────────────────────────────┘ │
│ │
└──────────────────────────────────┘
18. O que está acontecendo por baixo?
Quando acessamos:
http://localhost:4200/mfe-1
o Angular Shell não possui necessariamente o código do mfe-1 no bundle inicial.
O fluxo é:
Browser
│
▼
Angular Shell
│
▼
Router
│
▼
loadRemoteModule()
│
▼
federation.manifest.json
│
▼
mfe-1
│
▼
remoteEntry.json
│
▼
./Component
│
▼
AppComponent
Isso permite que o Micro Frontend seja desenvolvido e publicado separadamente.
19. Compartilhamento de dependências
Outro ponto importante da Federation é o compartilhamento de dependências.
Nossa configuração possui:
shared: {
...shareAll({
singleton: true,
strictVersion: true,
requiredVersion: 'auto',
}),
}
Isso permite compartilhar bibliotecas entre o Shell e os Micro Frontends.
Por exemplo:
Shell
└── Angular
MFE-1
└── Angular
MFE-2
└── Angular
Em vez de necessariamente carregar múltiplas cópias das mesmas dependências, o Federation pode compartilhar essas bibliotecas.
O singleton: true é especialmente importante para bibliotecas que devem possuir uma única instância no runtime, como as principais bibliotecas do Angular.
20. Por que utilizar Native Federation no Angular 21?
No artigo original utilizamos:
Webpack Module Federation
Essa abordagem continua sendo importante e o ecossistema de Module Federation continua evoluindo.
Porém, para um projeto novo utilizando Angular 21, o Native Federation é uma opção muito interessante porque:
- integra-se ao Application Builder do Angular;
- utiliza esbuild;
- trabalha com ES Modules;
- utiliza Import Maps;
- não depende diretamente do Webpack;
- mantém o modelo mental do Module Federation;
- suporta Standalone Components;
- suporta SSR;
- possui versões alinhadas às versões do Angular.
21. Estrutura final
Ao final desta primeira parte teremos:
artigo-mfe/
│
├── mfe-host/
│ │
│ ├── src/
│ │ ├── app/
│ │ │ ├── app.component.ts
│ │ │ └── app.routes.ts
│ │ │
│ │ ├── assets/
│ │ │ └── federation.manifest.json
│ │ │
│ │ ├── main.ts
│ │ └── bootstrap.ts
│ │
│ ├── federation.config.js
│ └── package.json
│
├── mfe-1/
│ │
│ ├── src/
│ │ └── app/
│ │ └── app.component.ts
│ │
│ ├── federation.config.js
│ └── package.json
│
└── mfe-2/
│
├── src/
│ └── app/
│ └── app.component.ts
│
├── federation.config.js
└── package.json
Conclusão
Neste artigo configuramos uma arquitetura de Micro Frontends utilizando Angular 21 + Native Federation.
Criamos:
Angular 21
│
▼
┌─────────────┐
│ HOST │
│ :4200 │
└──────┬──────┘
│
┌──────────┴──────────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ MFE-1 │ │ MFE-2 │
│ :4201 │ │ :4202 │
└─────────────┘ └─────────────┘
O Shell é responsável pela experiência principal da aplicação, enquanto os Micro Frontends podem ser desenvolvidos e publicados de forma independente.
Na próxima parte podemos avançar para uma arquitetura mais próxima de um cenário real, trabalhando com:
- múltiplas páginas dentro de cada Micro Frontend;
-
loadChildren()com rotas remotas; - comunicação entre Shell e Micro Frontends;
- compartilhamento de serviços;
- autenticação;
- Angular Material;
- gerenciamento de estado;
- deploy independente;
- ambientes
DEV,HMLePROD; - versionamento dos Micro Frontends;
- tratamento de indisponibilidade de um Remote;
- arquitetura recomendada para projetos corporativos.
Troubleshooting — Problemas na instalação do Native Federation
Durante a configuração do Native Federation, o comando apresentado no artigo pode apresentar erro dependendo da versão do Angular instalada no ambiente.
O comando inicial utilizado no artigo é:
ng add @angular-architects/native-federation --project mfe-remote-2 --port 4201 --type remote
Em alguns ambientes, o Angular CLI pode não conseguir encontrar automaticamente uma versão compatível do pacote.
Um exemplo de erro é:
Could not find a compatible version with `latest`.
Searching for a compatible version.
Cannot read properties of undefined (reading 'latest')
An unhandled exception occurred:
Cannot read properties of undefined (reading 'latest')
Esse problema está relacionado à resolução automática da versão do pacote e não necessariamente a um problema no projeto Angular.
1. Verifique a versão do Angular
Antes de corrigir o problema, execute:
ng version
Por exemplo:
Angular CLI : 21.2.25
Angular : 21.2.25
Node.js : 24.15.0
Package Manager : npm 12.2.0
A versão do Angular deve ser utilizada para escolher uma versão compatível do Native Federation.
2. Instale uma versão compatível manualmente
Caso o ng add apresente o erro descrito acima, em vez de utilizar o latest, instale explicitamente uma versão compatível.
Para um projeto utilizando Angular 21.2.x, por exemplo:
npm install @angular-architects/native-federation@21.2.6 --save-dev
Depois execute o schematic:
ng generate @angular-architects/native-federation:init \
--project mfe-remote-2 \
--port 4201 \
--type remote
No Windows/Git Bash, o comando também pode ser executado em uma única linha:
ng generate @angular-architects/native-federation:init --project mfe-remote-2 --port 4201 --type remote
3. Regra geral
A ideia é utilizar uma versão do Native Federation compatível com a versão principal do Angular do projeto.
Por exemplo:
Angular 21.x
↓
Native Federation 21.x
Portanto, se estiver utilizando outra versão do Angular, primeiro verifique:
ng version
e então consulte a versão do @angular-architects/native-federation compatível com essa versão.
Evite simplesmente instalar a versão latest quando o Angular CLI não conseguir determinar automaticamente uma versão compatível.
4. Caso tenha instalado uma versão incorreta
Se uma tentativa anterior instalou uma versão incompatível, remova o pacote:
npm uninstall @angular-architects/native-federation
Limpe a instalação:
rm -rf node_modules package-lock.json
No Windows PowerShell, utilize:
Remove-Item -Recurse -Force node_modules
Remove-Item package-lock.json
Depois reinstale as dependências:
npm install
E instale novamente a versão compatível:
npm install @angular-architects/native-federation@21.2.6 --save-dev
Por fim:
ng generate @angular-architects/native-federation:init --project mfe-remote-2 --port 4201 --type remote
5. Resumo
Se o comando original funcionar:
ng add @angular-architects/native-federation --project mfe-remote-2 --port 4201 --type remote
continue normalmente com o tutorial.
Caso apareça:
Could not find a compatible version with `latest`
faça:
ng version
identifique a versão do Angular e instale manualmente uma versão compatível do Native Federation:
npm install @angular-architects/native-federation@<versao-compativel> --save-dev
e execute o schematic:
ng generate @angular-architects/native-federation:init \
--project mfe-remote-2 \
--port 4201 \
--type remote
Importante: as versões dos pacotes podem mudar ao longo do tempo. Portanto, sempre verifique a compatibilidade entre a versão do Angular instalada no projeto e a versão do Native Federation utilizada.
Referências
- Artigo original: Desacoplando frontend com module-federation Angular - Parte 1.
- Angular Architects — Micro Frontends with Modern Angular.
- Angular Architects — Native Federation.
- Documentação do
@angular-architects/native-federation.
Top comments (0)