Skip to content

Plugin architecture with Webpack 5, NX 15 and Angular 14+ (Ivy and AOT)

Notifications You must be signed in to change notification settings

faileon/angular-module-federation

Repository files navigation

Plugin Architektura s Webpack5, NX a Angular 14+ (Ivy + AOT)

Jak spustit:

  1. Vybuildit plugin a widget (mělo by být součástí gitu, lze přeskočit na krok 3.)
    1. npm run build:remotes
  2. přetáhnout obsah složky dist/apps do apps/shell/assets (v assets tedy bude složka plugins a widgets)
  3. spustit aplikaci pomocí: nx run shell:serve:production
  4. otevřít aplikaci

  1. Alternativně lze vše servnout s hot reloadem:
    1. npm run start:all
  2. otevřít aplikaci

Jak to funguje

  • Využívá Module Federation koncept webpacku
  • Pro správné načítání modulů je nutné, aby webpack věděl, kde se daný modul nachází - k tomu slouží funkce setRemoteDefinitions, která se aktuálně volá při bootstrapu shell aplikace v apps/shell/src/main.ts
    • Tento manifest lze ale nastavit kdykoliv, jediná podmínka je, aby se tak stalo ještě před tím, než se pokusíme naloadovat modul pomoci funkce loadRemoteModule
  • Každý plugin si ve svém souboru module-federation.config.js může nastavit své entry pointy pomocí atributu exposes. Může to být modul, komponenta, cesty pro ng-router...
  • Aktuálně se například v apps/shell/src/app/app.routes.ts stahuje modul pluginu calendar, který zaregistruje své cesty do ng-routeru.
    • Nyní jsem se nezabýval implementací pro správu pluginu, takže cesta calendar je hardcoded, není ovšem problém tyto cesty nastavit dynamicky, např. po fetchnuti z BE.
  • V aplikaci apps/shell/src/app/app.component.ts se take renderuji 2 komponenty - jedna z pluginu calendar a druha z widgetu, jež je standalone builditelna komponenta.
    • Opět je to zde mírně hardoced, ale analogicky jako s cestami calendar pluginu, není problém tyto informace nastavit dynamicky, např. po fetchnuti z BE.

Výhody

  • pro načtení pluginu se používá funkce z NX @nrwl/angular/mf/loadRemoteModule, která pod pokličkou používá webpack a jejich ModuleFederation plugin
    • heavy lifting tedy dělá NX, potažmo webpack
    • kompatibilní s NX incremental builds
  • Díky webpacku chunkování funguje rekurzivně - lazy loading tedy funguje kdekoliv, i v dynamicky načteném pluginu
  • Samostatné pluginy a widgety lze servnout, tím pádem je velmi jednoduchý jejich developement. Lze je take vybuildit a zazipovat pro účel pluginizace (inspirace názvu z deduplikace).
    • Možnost snadné implementace e2e testu na jednotlivé pluginy

Nevyhody

  • zatím jsem na nic neřešitelného nenarazil

NX's withModuleFederation

  • Opinionated wrapper na ModuleFederationPlugin webpacku
  • Výchozí sdílené dependencies jsou:
    • @angular/animations
    • @angular/common
    • npm packages (podle root package.json)
    • všechny @nrwl/workspace:library
    • dependencies co vyčte z projectGraphu, tzn. pokud naimportuju nejakou knihovnu z monorepa, sam ji prida do sharovanych dependencies. Zpravidla je to žádoucí chování, lze tomu ale zabránit v configu.

Verzování

  • své verze si každý plugin a appka udržují ve svém package.json
  • appka nebo plugin si v souboru module-federation.config.js mohou dale specifikovat, které verze jednotlivých pluginu potřebují, např.:
  {
  // ...
  additionalShared: [
    ['@ng-mfa/shared/data-access/random', {singleton: true, requiredVersion: '~1.0.0', strictVersion: true}]
  ]
}
  • Pokud dojde k neshodě smluvených verzí, upozorní nás webpack následujícím runtime errorem:
    • Unsatisfied version 2.0.0 from shell of shared singleton module @ng-mfa/shared/data-access/random (required >=1.0.0 <1.1.1)
  • requiredVersion lze specifikovat v několika formátech:
    • standardní formát s ~ nebo ^, např.:
      • ~1.0.0, nebo-li 1.0.x
      • ^1.0.0, nebo-li 1.x.x
    • rozsah, např.:
      • '>=1.0.0 <1.1.1', nebo-li verze od 1.0.0 (včetně) až 1.1.1 (vyjma)

Commands

  • nový plugin:
    • npx nx g @nrwl/angular:remote plugins/<PLUGIN_NAME>
  • nový widget jako standalone component:
    • npx nx g @nrwl/angular:remote widgets/<WIDGET_NAME> --standalone

Source codes

TODO:

  • verify lazy loading inside plugins works
  • exposing component from a plugin and loading it elsewhere (shell)
  • standalone components (widgets)
  • create shared lib with some state and verify it's shared in app and plugins
  • versioning and shared modules, conflicts, etc.
  • dynamically create routes (requires implementing plugin management with backend, omitted for now)
  • build and zip automatically (streamline the process - so we don't have to zip manually)
  • verify that errors in plugins don't crash shell app, if so, implement error catching (perhaps outside ng-zone?)

About

Plugin architecture with Webpack 5, NX 15 and Angular 14+ (Ivy and AOT)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published