Frontend

Micro Front-end com Module Federation: Estratégias Avançadas e Deploy

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

Micro Front-end com Module Federation: Estratégias Avançadas e Deploy
Photo by Jack B / Unsplash

1. Versionamento e Independência de Remotes

Em projetos grandes, cada Remote Application deve poder evoluir de forma independente. Para isso:

1.1 Versão de Módulos Remotos

No webpack.config.js do Remote, podemos definir versões:

module.exports = withModuleFederationPlugin({
  name: 'mfe-dashboard-one',
  filename: 'remoteEntry.js',
  exposes: { './Component': './src/app/app.component.ts' },
  shared: {
    ...shareAll({
      singleton: true,
      strictVersion: true,
      requiredVersion: '^1.0.0', // versão específica do remote
    }),
  },
});
  • requiredVersion garante que o host carrega a versão correta do remote.
  • Atualizações no remote podem ser liberadas sem impactar a aplicação host, desde que respeitem o contrato de versão.

1.2 Estratégia de Deploy Independente

Cada remote pode ter seu próprio pipeline de CI/CD, por exemplo:

  • Build independente: ng build mfe-dashboard-one --prod
  • Deploy em CDN ou servidor separado: /extensions/mfe-dashboard-one/remoteEntry.js
  • Atualização de rota no host: apenas atualizar remoteEntry no app.routes.ts ou routes.json.

Isso permite escalabilidade e independência entre equipes.


2. Rotas Dinâmicas Avançadas

Rotas dinâmicas permitem que o host descubra remotes em runtime, útil quando novos módulos são adicionados sem deploy do host.

2.1 Configuração de Route Loader

route-loader.service.ts

import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Routes } from '@angular/router';
import { map, Observable } from 'rxjs';
import { loadRemoteModule } from '@angular-architects/module-federation';

@Injectable({ providedIn: 'root' })
export class RouteLoaderService {
  private http = inject(HttpClient);

  loadRoutes(): Observable<Routes> {
    return this.http.get<any[]>('/routes.json').pipe(
      map((routes) => routes.map(route => ({
        path: route.path,
        loadComponent: () =>
          loadRemoteModule({
            type: route.type,
            remoteEntry: route.remoteEntry,
            exposedModule: route.exposedModule,
          }).then((m) => m.AppComponent),
      })))
    );
  }
}

routes.json

[
  {
    "path":"mfe-app-01",
    "type":"module",
    "remoteEntry":"http://localhost:3000/extensions/mfe-app-01/remoteEntry.js",
    "exposedModule":"./Component"
  },
  {
    "path":"mfe-app-02",
    "type":"module",
    "remoteEntry":"http://localhost:3000/extensions/mfe-app-02/remoteEntry.js",
    "exposedModule":"./Component"
  }
]
  • Permite adicionar novos remotes sem rebuild do host.
  • Facilita deploys independentes e versionamento de rotas.

3. Estratégias de CI/CD para Micro Front-ends

Para equipes grandes, cada MFE deve ter pipeline própria, mantendo host e remotes sincronizados:

  1. Build e Test Unitário: cada remote executa testes isolados.
  2. Lint e Code Analysis: garante consistência entre remotes.
  3. Deploy em CDN ou S3: remotes ficam disponíveis publicamente via URL.
  4. Integração do Host: host consome remotes via remoteEntry.
  5. Monitoramento: usar ferramentas como Sentry ou Datadog para erros em runtime.

Exemplo de fluxo de deploy em CI:

name: MFE Dashboard One CI/CD
on:
  push:
    branches:
      - main

jobs:
  build-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '20'
      - run: npm install
      - run: ng build mfe-dashboard-one --prod
      - run: aws s3 sync ./dist/mfe-dashboard-one s3://cdn.example.com/extensions/mfe-dashboard-one --delete
  • Cada remote é deployado separadamente, garantindo independência de release.

4. Troubleshooting e Debugging

MFE adiciona complexidade. Alguns problemas comuns:

  1. Erro de múltiplas versões do Angular
    • Use shared: shareAll({ singleton: true }) no webpack.
    • Verifique ng version e package.json.
  2. Rotas do remote não funcionam no host
    • Configurar APP_BASE_HREF no módulo do remote:
import { APP_BASE_HREF } from '@angular/common';

@NgModule({
  providers: [{ provide: APP_BASE_HREF, useValue: '/' }],
})
export class AppModule {}
  1. Falha no carregamento do remote
    • Verifique se remoteEntry.js está acessível.
    • Teste com navegador ou curl.
  2. Componentes não aparecem
    • Checar exposes no webpack do remote.
    • Confirmar nome do componente exposto (AppComponent ou outro).

5. Performance e Otimizações

  1. Lazy Loading obrigatório – só carregue o remote quando necessário.
  2. Tree Shaking – Angular e Webpack removem código não usado.
  3. Cacheamento do remoteEntry.js – usar headers HTTP: Cache-Control para evitar downloads repetidos.
  4. Compartilhamento de dependências – evita duplicação de Angular, RxJS e bibliotecas comuns.
  5. Monitoramento de bundle – usar webpack-bundle-analyzer para identificar módulos grandes.

6. Integração com Micro Front-end Dinâmico

Em grandes plataformas SaaS:

  • Cada equipe publica seu módulo remoto.
  • O host consome remotes via configuração central (routes.json).
  • Novos módulos podem ser adicionados sem rebuild do host.
  • Permite extensibilidade infinita, alinhada com arquiteturas plugin-based.

Exemplo de fluxo:

  1. Nova equipe cria mfe-new-dashboard.
  2. Publica em CDN com remoteEntry.js.
  3. Adiciona entrada em routes.json do host.
  4. Usuário final acessa o novo dashboard sem atualizar o host.

7. Comunicação Entre Módulos

Além de BehaviorSubject:

  • Services Singleton: compartilhados via shared no webpack.
  • Event Bus: pequenas mensagens via Subject ou RxJS.
  • LocalStorage/IndexedDB: compartilhamento de dados persistentes.
  • Custom Events: browser native events para módulos isolados.

Exemplo:

// shared-event-bus.ts
import { Subject } from 'rxjs';
export const eventBus = new Subject<{ type: string, payload?: any }>();
  • Host e remotes podem se inscrever no eventBus.
  • Permite comunicação desacoplada sem dependência direta.

Conclusão

Nesta etapa avançada, abordamos:

  • Versionamento e deploy independente de remotes.
  • Rotas dinâmicas e runtime discovery.
  • Estratégias de CI/CD para equipes distribuídas.
  • Troubleshooting e boas práticas de debugging.
  • Otimização de performance e compartilhamento de dependências.
  • Comunicação eficiente entre host e remotes.

Essas técnicas permitem que equipes grandes construam sistemas escaláveis, modulares e de fácil manutenção, respeitando os princípios de micro front-end e Module Federation.

Você também pode gostar