Arquitectura de Microfrontends con Angular: Guía Práctica

15 de junio de 2025
Osman Jimenez
Angular Arquitectura de Software Microfrontends

¿Qué son los Microfrontends?

Los microfrontends aplican el concepto de microservicios al frontend, permitiendo que equipos independientes desarrollen, desplieguen y escalen diferentes partes de una aplicación web de forma autónoma.

¿Cuándo usar Microfrontends?

Usa microfrontends cuando:

  • Tienes equipos grandes trabajando en la misma aplicación
  • Diferentes partes de la app tienen ciclos de release independientes
  • Quieres migrar gradualmente de una tecnología a otra
  • Necesitas escalar equipos de forma independiente

No uses microfrontends si:

  • Tienes un equipo pequeño (menos de 10 personas)
  • Tu aplicación es simple y no requiere escalabilidad compleja
  • No tienes la infraestructura para manejar la complejidad adicional

Estrategias de Implementación

1. Module Federation (Webpack 5)

La solución más popular para Angular:

// webpack.config.js del host
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        mfe1: 'mfe1@http://localhost:4201/remoteEntry.js',
        mfe2: 'mfe2@http://localhost:4202/remoteEntry.js'
      },
      shared: {
        '@angular/core': { singleton: true, strictVersion: true },
        '@angular/common': { singleton: true, strictVersion: true },
        '@angular/router': { singleton: true, strictVersion: true }
      }
    })
  ]
};

2. Routing Dinámico

// app.routes.ts
import { loadRemoteModule } from '@angular-architects/module-federation';

export const routes: Routes = [
  {
    path: 'dashboard',
    loadChildren: () => loadRemoteModule({
      type: 'module',
      remoteEntry: 'http://localhost:4201/remoteEntry.js',
      exposedModule: './Module'
    }).then(m => m.DashboardModule)
  },
  {
    path: 'profile',
    loadChildren: () => loadRemoteModule({
      type: 'module',
      remoteEntry: 'http://localhost:4202/remoteEntry.js',
      exposedModule: './Module'
    }).then(m => m.ProfileModule)
  }
];

Comunicación entre Microfrontends

1. Eventos Personalizados

// Emisor (MFE1)
window.dispatchEvent(new CustomEvent('user-updated', {
  detail: { userId: 123, name: 'Osman' }
}));

// Receptor (MFE2)
window.addEventListener('user-updated', (event: any) => {
  console.log('Usuario actualizado:', event.detail);
});

2. Shared State Service

// shared-state.service.ts
@Injectable({ providedIn: 'root' })
export class SharedStateService {
  private userSubject = new BehaviorSubject<User | null>(null);
  user$ = this.userSubject.asObservable();

  setUser(user: User) {
    this.userSubject.next(user);
  }
}

// Uso en cualquier MFE
constructor(private sharedState: SharedStateService) {
  this.sharedState.user$.subscribe(user => {
    console.log('Usuario:', user);
  });
}

Gestión de Estilos Compartidos

// styles.scss compartido
:root {
  --primary-color: #3f51b5;
  --secondary-color: #ff4081;
  --font-family: 'Roboto', sans-serif;
}

// Cada MFE importa los estilos base
@import 'shared-styles/variables';
@import 'shared-styles/mixins';

// Y define sus propios estilos
.mfe-specific-class {
  color: var(--primary-color);
}

Versionado y Compatibilidad

// package.json de cada MFE
{
  "name": "@myapp/mfe-dashboard",
  "version": "1.2.3",
  "peerDependencies": {
    "@angular/core": "^17.0.0",
    "@myapp/shared-lib": "^2.0.0"
  }
}

// El host valida versiones en runtime
if (!isCompatibleVersion(mfeVersion, requiredVersion)) {
  console.error('Versión incompatible del microfrontend');
  // Cargar versión de fallback o mostrar error
}

Testing de Microfrontends

Tests Unitarios

// Cada MFE tiene sus propios tests
describe('DashboardComponent', () => {
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [DashboardComponent],
      providers: [MockSharedStateService]
    }).compileComponents();
  });
  
  it('should display user data', () => {
    // Test específico del MFE
  });
});

Tests de Integración

// cypress/e2e/microfrontends.cy.ts
describe('Microfrontends Integration', () => {
  it('should navigate between MFEs', () => {
    cy.visit('/');
    cy.get('[data-test="dashboard-link"]').click();
    cy.url().should('include', '/dashboard');
    cy.get('[data-test="dashboard-content"]').should('be.visible');
  });
  
  it('should share state between MFEs', () => {
    cy.visit('/profile');
    cy.get('[data-test="update-name"]').type('Osman');
    cy.visit('/dashboard');
    cy.get('[data-test="user-name"]').should('contain', 'Osman');
  });
});

Deployment y CI/CD

# .github/workflows/deploy-mfe.yml
name: Deploy Microfrontend

on:
  push:
    branches: [main]
    paths:
      - 'apps/mfe-dashboard/**'

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Build MFE
        run: |
          cd apps/mfe-dashboard
          npm ci
          npm run build
      - name: Deploy to CDN
        run: |
          aws s3 sync dist/ s3://my-bucket/mfe-dashboard/
          aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DIST_ID }}

Mejores Prácticas

  1. Define contratos claros: Documenta las interfaces entre microfrontends
  2. Versiona tus MFEs: Usa semantic versioning para gestionar cambios
  3. Monitorea el rendimiento: Los MFEs pueden impactar el tiempo de carga
  4. Implementa fallbacks: Maneja errores cuando un MFE no carga
  5. Comparte solo lo necesario: Evita dependencias innecesarias entre MFEs

Herramientas Recomendadas

  • @angular-architects/module-federation: Wrapper para Module Federation
  • Nx: Monorepo tool con soporte para microfrontends
  • Single-SPA: Framework agnóstico para microfrontends
  • Bit: Compartir componentes entre MFEs

Conclusión

Los microfrontends son una solución poderosa para aplicaciones grandes y equipos distribuidos, pero vienen con complejidad adicional. Evalúa cuidadosamente si tu proyecto realmente los necesita. Si decides implementarlos, Module Federation con Angular es una excelente opción que ofrece un buen balance entre flexibilidad y simplicidad.

¿Has implementado microfrontends en tu proyecto? ¿Qué desafíos has enfrentado?