Micro Front-end com Module Federation: Implementação Prática em Angular

Parte 3 - Micro Front-end com Module Federation: Construindo Aplicações Extensíveis

Micro Front-end com Module Federation: Implementação Prática em Angular
Photo by Caspar Camille Rubin / Unsplash

Introdução

Nesta seção, construiremos um exemplo completo de micro front-end em Angular utilizando Module Federation. A ideia é criar:

  1. Host Application – a aplicação principal que consome os módulos remotos.
  2. Remote Application Dashboard One – um módulo remoto com um gráfico de barras.
  3. Remote Application Dashboard Two – um módulo remoto com um gráfico de pizza.

Além disso, exploraremos:

  • Configuração do Webpack para Module Federation.
  • Rotas dinâmicas e carregamento assíncrono de módulos.
  • Comunicação entre host e remotes.
  • Boas práticas para projetos reais.

1. Setup do Projeto

Requisitos

  • Angular 18
  • Node 20.17.0
  • Bibliotecas:
    • @angular-architects/module-federation
    • chart.js

Comandos iniciais

Host Application

ng new mfe-app-host
cd mfe-app-host
ng add @angular-architects/module-federation --project mfe-app-host --port 4200 --type host --skip-confirmation

Remote Application Dashboard One

ng new mfe-dashboard-one
cd mfe-dashboard-one
ng add @angular-architects/module-federation --project mfe-dashboard-one --port 4201 --type remote --skip-confirmation
npm install chart.js

Remote Application Dashboard Two

ng new mfe-dashboard-two
cd mfe-dashboard-two
ng add @angular-architects/module-federation --project mfe-dashboard-two --port 4202 --type remote --skip-confirmation
npm install chart.js

2. Host Application – Estrutura e Componentes

O host será responsável por integrar os módulos remotos e gerenciar a navegação entre eles.

2.1 Componente Home

home.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-home',
  standalone: true,
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css'],
})
export class HomeComponent {}

home.component.html

<h1>Home</h1>
<p>Bem-vindo ao Host Application!</p>

2.2 Componente Navbar

navbar.component.ts

import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';

@Component({
  selector: 'app-navbar',
  standalone: true,
  imports: [RouterLink],
  templateUrl: './navbar.component.html',
  styleUrls: ['./navbar.component.css'],
})
export class NavbarComponent {}

navbar.component.html

<nav class="navbar">
  <div class="logo">
    <a href="#home">MFE</a>
  </div>
  <div class="nav-links">
    <a routerLink="/home">Home</a>
    <a routerLink="/mfe-dashboard-one">Dashboard One</a>
    <a routerLink="/mfe-dashboard-two">Dashboard Two</a>
  </div>
</nav>

navbar.component.css

.navbar {
  background-color: #333;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 20px;
}
.navbar a {
  color: white;
  text-decoration: none;
  padding: 14px 16px;
}
.navbar a:hover {
  background-color: #ddd;
  color: black;
}
.logo {
  font-size: 24px;
  font-weight: bold;
}
.nav-links {
  display: flex;
}
@media screen and (max-width: 600px) {
  .navbar {
    flex-direction: column;
  }
  .nav-links {
    flex-direction: column;
    width: 100%;
  }
  .navbar a {
    text-align: center;
  }
}

2.3 App Component

app.component.ts

import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { NavbarComponent } from './navbar/navbar.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet, NavbarComponent],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {}

app.component.html

<app-navbar></app-navbar>
<router-outlet></router-outlet>

2.4 Rotas

O host precisa carregar dinamicamente os módulos remotos usando loadRemoteModule da biblioteca @angular-architects/module-federation.

app.routes.ts

import { Routes } from '@angular/router';
import { loadRemoteModule } from '@angular-architects/module-federation';
import { HomeComponent } from './home/home.component';

export const routes: Routes = [
  {
    path: 'mfe-dashboard-one',
    loadComponent: () =>
      loadRemoteModule({
        type: 'module',
        remoteEntry: 'http://localhost:4201/remoteEntry.js',
        exposedModule: './Component',
      }).then((m) => m.AppComponent),
  },
  {
    path: 'mfe-dashboard-two',
    loadComponent: () =>
      loadRemoteModule({
        type: 'module',
        remoteEntry: 'http://localhost:4202/remoteEntry.js',
        exposedModule: './Component',
      }).then((m) => m.AppComponent),
  },
  { path: 'home', component: HomeComponent },
  { path: '', redirectTo: '/home', pathMatch: 'full' },
];

2.5 Configuração do Webpack

webpack.config.js

const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({
  remotes: {
    mfeDashboardOne: 'http://localhost:4201/remoteEntry.js',
    mfeDashboardTwo: 'http://localhost:4202/remoteEntry.js',
  },
  shared: {
    ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }),
  },
});
  • remotes: Lista de micro front-ends que serão carregados.
  • shared: Dependências compartilhadas entre host e remotes.

3. Remote Application Dashboard One

O Dashboard One será um micro front-end independente, com um gráfico de barras usando Chart.js.

3.1 App Component

app.component.ts

import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { Chart } from 'chart.js/auto';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  ngOnInit() {
    this.createBarChart();
  }

  createBarChart() {
    new Chart('barChart', {
      type: 'bar',
      data: {
        labels: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho'],
        datasets: [
          {
            label: 'Vendas',
            data: [12, 19, 3, 5, 2, 3],
            backgroundColor: 'rgba(75, 192, 192, 0.6)',
          },
        ],
      },
      options: { responsive: true, scales: { y: { beginAtZero: true } } },
    });
  }
}

app.component.html

<div class="dashboard">
  <h1>Dashboard One</h1>
  <div class="chart-container">
    <canvas id="barChart"></canvas>
  </div>
</div>

app.component.css

.dashboard { padding: 20px; }
.chart-container { width: 100%; max-width: 600px; margin-bottom: 20px; }

3.2 Webpack Configuration

const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({
  name: 'mfe-dashboard-one',
  exposes: { './Component': './src/app/app.component.ts' },
  shared: { ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }) },
});

4. Remote Application Dashboard Two

O Dashboard Two será um gráfico de pizza, também carregado dinamicamente pelo host.

4.1 App Component

app.component.ts

import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { Chart } from 'chart.js/auto';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  ngOnInit() {
    this.createPieChart();
  }

  createPieChart() {
    new Chart('pieChart', {
      type: 'pie',
      data: {
        labels: ['Vermelho', 'Azul', 'Amarelo'],
        datasets: [
          { label: 'Cores Favoritas', data: [300, 50, 100], backgroundColor: ['rgb(255, 99, 132)','rgb(54, 162, 235)','rgb(255, 205, 86)'] }
        ]
      },
      options: { responsive: true }
    });
  }
}

app.component.html

<div class="dashboard">
  <h1>Dashboard Two</h1>
  <div class="chart-container">
    <canvas id="pieChart"></canvas>
  </div>
</div>

4.2 Webpack Configuration

const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({
  name: 'mfe-dashboard-two',
  exposes: { './Component': './src/app/app.component.ts' },
  shared: { ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }) },
});

5. Comunicação entre Host e Remotes

Para compartilhar estado, podemos criar um BehaviorSubject em um pacote compartilhado.

5.1 Shared State

// shared-state.ts
import { BehaviorSubject } from 'rxjs';

export const userState = new BehaviorSubject<{ name: string }>({ name: '' });
  • Host e Remotes importam userState.
  • Qualquer alteração no BehaviorSubject é refletida em tempo real em todos os micro front-ends.

6. Testando a Aplicação

  1. Rodar os três projetos simultaneamente:
ng serve mfe-app-host
ng serve mfe-dashboard-one
ng serve mfe-dashboard-two
  1. Abrir http://localhost:4200.
  2. Navegar entre Home, Dashboard One e Dashboard Two.
  3. Confirmar que gráficos são renderizados corretamente e que não há duplicação de dependências.

7. Boas Práticas

  1. Single Version de bibliotecas compartilhadas para evitar bugs.
  2. Componentes standalone para fácil integração.
  3. Lazy Loading de módulos para performance.
  4. Documentar remotes: URLs, módulos expostos, dependências.
  5. Testes de integração entre host e remotes.

Conclusão

Nesta parte, construímos uma aplicação Angular com host e dois remotes, usando Module Federation. Aprendemos:

  • Configuração do webpack e Module Federation.
  • Rotas dinâmicas e carregamento assíncrono.
  • Criação de dashboards interativos com Chart.js.
  • Compartilhamento de estado e boas práticas para micro front-ends.

Na Parte 4, vamos abordar estratégias avançadas, incluindo deploys independentes, versionamento, troubleshooting e integração com CI/CD.

La búsqueda gana coherencia cuando se determina la prioridad entre colección, uso habitual y exposición. La ficha del producto debería permitir comprobar si existen notas especiales sobre el cuidado de los estampados. El recurso «» puede resultar práctico al comprobar los datos visibles sin hacer suposiciones. El último paso es comprobar el plazo de preparación y entrega.

La búsqueda gana coherencia cuando se determina la prioridad entre colección, uso habitual y exposición. La ficha del producto debería permitir comprobar si existen notas especiales sobre el cuidado de los estampados. El recurso «equipaciones de la Serie A» puede resultar práctico al comprobar los datos visibles sin hacer suposiciones. El último paso es comprobar el plazo de preparación y entrega.

Com a aproximação de um evento, é útil analisar os aspectos ligados a fantasias de cosplay femininas com cuidado desde o início. Conhecendo melhor a obra original, analisar os aspectos ligados a fantasias de cosplay também ajuda a avaliar conforto e movimento. Para comparar detalhes práticos sobre os aspectos ligados a sapatos de cosplay, perucas resistentes ao calor com opções de tamanho ajuda a limitar as alternativas para cada necessidade. Para reutilizar as peças em outros eventos, convém adaptar os aspectos ligados a sapatos de cosplay às necessidades pessoais.