Skip to content

Latest commit

 

History

History
150 lines (94 loc) · 5.73 KB

File metadata and controls

150 lines (94 loc) · 5.73 KB

CLAUDE.md – poc-angular-microfrontend

Microfrontend-POC mit Angular, Webpack Module Federation und Angular Elements (Web Components). Drei vollständig unabhängige Angular-Anwendungen mit verschiedenen Angular-Versionen.

Projektstruktur

poc-angular-microfrontend/
├── shell/    AppShell – Angular 19, Port 4200
├── remote/   Remote "Produkte" – Angular 18, Port 4201
├── orders/   Remote "Bestellungen" – Angular 17, Port 4202
└── docs/
    └── architecture.md   Vollständige Architekturdokumentation

Wichtig: kein gemeinsames package.json

Jede App hat ihr eigenes node_modules/. Befehle immer im jeweiligen App-Verzeichnis ausführen.

Apps starten

cd remote && npm start    # http://localhost:4201
cd orders && npm start    # http://localhost:4202
cd shell  && npm start    # http://localhost:4200

Shell startet auch ohne laufende Remotes (APP_INITIALIZER fängt Fehler ab).

Bauen

cd <app> && npx ng build --configuration development

Versionen

App Angular TypeScript
shell 19 5.7
remote 18 5.5
orders 17 5.2

Angular ist nicht als shared konfiguriert – jede App bundelt ihre eigene Version. Nur zone.js ist Singleton. Bei Versionsänderungen muss TypeScript mitgezogen werden (Angular 17 → TS <5.3, Angular 18 → TS <5.6, Angular 19 → TS <5.8).


Architekturmuster

Dynamische Integration

Neue Remotes ohne Shell-Codeänderungen hinzufügen:

  1. Remote-App erstellen (Angular-App + Module Federation + register.ts + AppModule)
  2. metadata.ts anlegen: { elementName, routePath, navLabel } – kein Import, ~640 Byte
  3. webpack.config.js: ./metadata und ./register exposen
  4. Eintrag in shell/public/mfe-manifest.json

Custom Elements (Angular Elements)

AppModule.ngDoBootstrap()
    → createCustomElement(FeatureComponent, { injector })
    → customElements.define('mfe-<name>', ...)

@Input() → HTML-Attribute, @Output() → CustomEvents.

Kommunikation Remote → Shell

@Output() mfeAction.emit({ type, payload })
    → CustomEvent 'mfeAction'
    → DynamicMfeWrapperComponent.addEventListener + NgZone.run()
    → EventBusService.emit()
    → Subscriber (z. B. HeaderComponent)

Kommunikation Shell → Remote

HTML-Attribute auf dem Custom Element: el.setAttribute('currentUser', 'Max').

MfeRegistryService

Lädt mfe-manifest.json per APP_INITIALIZER, importiert ./metadata von jedem Remote (ohne Angular-Bundle zu laden), hält Einträge als BehaviorSubject<MfeEntry[]>.

DynamicMfeWrapperComponent

Generischer Wrapper für alle Remotes. Liest routePath aus ActivatedRoute.paramMap (Observable, nicht snapshot – sonst kein Reload bei Navigation), erstellt Custom Element via document.createElement(), mountet es in Container-div.

EventBusService

type ist string (kein Union-Typ) – Remotes können beliebige Event-Typen emittieren ohne Shell-Änderung.


Bekannte Fallstricke

NG0909 – bootstrapModule in der Angular-Zone

platformBrowserDynamic().bootstrapModule() darf nicht innerhalb einer Zone aufgerufen werden. Lösung: NgZone.runOutsideAngular(() => m.registerRemote()) in MfeLoaderService.

CustomEvent-Listener in der Remote-Zone

Listener läuft in Remote-Zone → Shell-Change-Detection wird nicht getriggert. Lösung: NgZone.run(() => this.eventBus.emit(event)).

Angular-Router: Komponenten-Wiederverwendung

Bei /mfe/products/mfe/orders wird DynamicMfeWrapperComponent wiederverwendet, ngOnInit nicht erneut aufgerufen. Lösung: route.paramMap als Observable abonnieren, bei Wechsel altes Element entfernen und neues mounten.

CORS für remoteEntry.js

remoteEntry.js ist ein ES-Modul. Dynamischer import() ist Cross-Origin → CORS-Header nötig. Konfiguriert in angular.json unter serve.options.headers.

tsconfig.app.json – Einstiegspunkte explizit listen

register.ts und metadata.ts liegen nicht im Import-Baum von main.ts → explizit in files[] aufnehmen, sonst vom TS-Compiler ignoriert.

outDir nicht in Root-tsconfig

Nur in tsconfig.app.json und tsconfig.spec.json, nicht in Root-tsconfig.json. Sonst VS-Code-Fehler TS6059.

Asset-Ordner: public/ nicht src/assets/

Angular 17+ verwendet public/ als Asset-Ordner. mfe-manifest.json liegt in shell/public/ und ist unter /mfe-manifest.json erreichbar (nicht /assets/mfe-manifest.json).


Framework-Unabhängigkeit

Die Shell kommuniziert nur über W3C-Standards (Custom Elements, HTML-Attribute, CustomEvents). React-, Vue- und Svelte-Remotes sind möglich – metadata.ts und das mfeAction-CustomEvent-Format sind Framework-agnostisch. zone.js wird von Nicht-Angular-Remotes ignoriert.

Geteiltes Design System

Web Components (@company/ui-kit, z. B. mit Lit gebaut) können über Module Federation als Singleton geteilt werden – im Gegensatz zu Angular selbst, das nicht geteilt wird. customElements.define wird global registriert und ist für alle Apps verfügbar. In webpack.config.js aller Apps: '@company/ui-kit': { singleton: true, strictVersion: false }.


Neues Remote hinzufügen – Kurzreferenz

  1. App anlegen (eigenen Port wählen)
  2. metadata.ts anlegen – { elementName, routePath, navLabel }, keine Imports
  3. register.ts – exportiert registerRemote()
  4. AppModuleDoBootstrap + createCustomElement
  5. webpack.config.js./register und ./metadata exposen, nur zone.js sharen
  6. tsconfig.app.json – beide Dateien in files[]
  7. angular.json – CORS-Header, tsconfig.json – kein outDir
  8. shell/public/mfe-manifest.json – neuen Eintrag ergänzen

Vollständige Dokumentation: docs/architecture.md