Micro Frontends – SAP Luigi, Module Federation und wie man damit arbeitet

In einer sich ständig weiterentwickelnden digitalen Landschaft suchen Unternehmen nach effizienten und flexiblen Lösungen für die Frontend-Entwicklung, um den Anforderungen einer dynamischen Marktnachfrage gerecht zu werden. In diesem Artikel werfen wir einen umfassenden Blick auf die Vor- und Nachteile von Micro Frontends als eine innovative Herangehensweise an die Frontend-Architektur. Dabei richten wir unseren Fokus vor allem auf das Micro Frontend Framework SAP Luigi sowie die Integration von Module Federation in der Micro Frontend Entwicklung. Neben technischen Aspekten diskutieren wir auch die Bedeutung von BDD (Behavior-Driven Development), TDD (Test-Driven Development) und DDD (Domain-Driven Design) im Kontext von Micro Frontends.

Micro Frontends

Micro Frontends sind die Micro Services der Frontend-Entwicklung. Sie übernehmen das Konzept der Micro Services, indem sie große monolithische Webanwendungen in Teile aufgliedern. So werden durch Micro Frontends die Benutzeroberflächen von Webanwendungen in kleinere, eigenständige Module aufgeteilt. Diese können von unterschiedlichen Teams entwickelt, gestaltet und gepflegt werden. Es sind selbstständige Module, die, wie Micro-Services, auf einem eigenen Host jeweils laufen. Die Anwendung, die die Micro Frontends konsumiert, wird auch gerne als Orchestrator bezeichnet. Dieser hat die Aufgabe, die Micro Frontends einzubinden und zu verwalten.

Wirklich sinnvoll?

Warum sollte man eine Anwendung mit Micro Frontends integrieren? Es scheint doch praktischer, alles in einem Projekt zu haben, oder?

Der erste Vorteil ist, dass jedes Micro Frontend einen eigenen Host hat. Das bietet zum einen die Möglichkeit, dass wenn das Micro Frontend aus welchen Gründen auch immer ausfällt, dass der Orchestrator auf Fallback-Optionen zurückgreifen, oder auch für die Dauer des Ausfalls ganz auf das Micro Frontend verzichten kann. Diesen Fall abzudecken, ist eine Aufgabe des Orchestrators. So ist aber eine höhere Laufstabilität für diesen gegeben. Dadurch, dass das Micro Frontend auf einem eigenen Host läuft, ist es möglich, die Gesamtanwendung, die dieses konsumiert, gezielter zu skalieren. So lässt sich das Micro Frontend bei höherer Nutzungslast skalieren, ohne alle anderen Komponenten mit zu skalieren. Das kann die Ressourcennutzung und somit die Kosten senken.

Jedes Micro Frontend hat zudem eine eigene Code-Basis, die lediglich die spezifische Aufgabe des Frontends zu erfüllen hat. Deshalb ist der Code deutlich kleiner und übersichtlicher als in einer monolithischen Anwendung. Das hat den Vorteil, dass das Micro Frontend nur begrenzt abhängig von anderen Modulen ist. Folglich ist es einfacher, den Code zu warten und Fehler zu beheben, da die Änderungen nur ein Frontend betreffen und nicht die anderen Teile der Anwendung. Dadurch, dass der Code kleiner ist, lässt sich dieser auch besser verstehen. Das hilft bei der Einführung neuer Teammitglieder in ein Projekt und vereinfacht die Pflege und Wartung. Da Micro Frontends unabhängig voneinander entwickelt werden, lassen sie sich auch unabhängig voneinander deployen. Ein weiterer Aspekt ist, dass sie sich technologieagnostisch integrieren lassen, das heißt, man kann die am besten für die Aufgabe des Micro Frontends geeignete Technologie verwenden.

Mögliche Nachteile von Micro Frontends

Da jedes Micro Frontend einen eigenen Host und eine eigene Code-Basis hat, muss für jeden Fall eine eigene Deployment-Pipeline integriert werden. So entsteht Mehraufwand, der mit bedacht werden muss. Auch entstehen Kosten durch das jeweilige Hosting, weil dafür eine ganze Instanz erstellt werden muss.
Das Nächste, was sich in der Entwicklung als störend erweisen kann ist, dass Code um das Micro Frontend herum entwickelt werden muss. Wenn ein Micro Frontend beispielsweise über ein iFrame integriert wird, dann muss man für die Kommunikation ein Event basiertes System aufsetzen, dass die postMessage Methode der iFrames verwendet. Dazu ist auch die Navigation in dem iFrame zu tracken, da sonst die Browser Navigations-Buttons nicht im Kontext der Micro Frontends funktionieren. Was die UX beeinträchtigt, da gewohnte Funktionen nicht verfügbar sind. Weiter ist zu bedenken, dass bei Integration mit einer Code teilenden Lösung Code-Kollisionen ausgelöst werden können. Etwa wenn zwei Micro Frontends dieselbe Klasse eines Elementes unterschiedlich designen und sich so gegenseitig überschreiben.

Da Micro Frontends unabhängig voneinander entwickelt werden, gilt es also darauf zu achten, das alle dieselbe UI/UX bieten. Dies lässt sich beispielsweise durch Frameworks wie Bootstrap garantieren, die das Styling übernehmen und durch eine entsprechende Konfiguration ein einheitliches Design erstellt wird. So stellt man sicher, dass sich das Micro Frontend in verschiedenen Anwendungen wiederverwenden lässt und verhindert zusätzlich Code-Kollisionen im Styling.

Der passende Use-Case

Eine Micro Frontend-Architektur ist u.a. eine gute Lösung für große Anwendungen, kann sich aber als ineffizient herausstellen, wenn diese für kleine, überschaubare Systeme eingesetzt wird.
Ein Einsatzbereich der Micro Frontends könnte zum Beispiel die Migration eines veralteten Monolithen sein. Dieser Monolith ist beispielsweise einfach zu groß geworden, hat zu viele technische Schulden aufgebaut und ist mittlerweile zu teuer und aufwändig in der Pflege. Welche Optionen stehen den Betreibern zur Verfügung? Die gesamte Anwendung von Grund auf neu aufzusetzen? Das birgt große Risiken. Micro Frontends können hier eine langsame und sichere Migration des Monolithen ermöglichen, indem Entwickler Stück für Stück einzelne Module aus dem Monolithen durch ein Micro Frontend ersetzen.

Frameworks erleichtern die Arbeit mit Micro Frontends

Micro Frontends bringen für jedes Projekt wiederholende Aufgaben mit sich. Um den Entwicklern bei der Lösung dieser Aufgaben zu helfen, gibt es Frameworks. Bekannt sind beispielsweise Single SPA, Piral oder auch Luigi von SAP. Was auch gerne erwähnt wird, wobei es sich aber nicht direkt um ein Framework handelt, ist Module Federation. Mit Module Federation wird den Entwicklern die Möglichkeit gegeben, JS-Komponenten als Module durch die Parametrisierung der Build-Konfiguration zu teilen. In diesem Artikel werden SAP Luigi und Module Federation oberflächlich verglichen.

SAP Luigi und Micro Frontends

Bei SAP Luigi handelt es sich um ein Micro Frontend Framework, das 2019 auf den Markt kam. Es ist ein Open Source-Projekt, das von SAP mit entwickelt wird. SAP Luigi bietet verschiedene Funktionen, die es unter anderem erlauben, eine Authentifizierung einzurichten, UI/UX-Features des Micro Frontend Orchestrators zu nutzen oder auch zwischen und auf den Micro Frontends zu navigieren. Dabei gibt es in SAP Luigi zwei Bereiche: den Luigi-Core, welcher die Orchestration der Micro Frontends übernimmt und den Luigi-Client, der die Kommunikation des Frontends mit dem Orchestrator ermöglicht. Um miteinander zu kommunizieren, verwendet SAP Luigi die „postMessage“-Funktion der iFrames.

Arbeit mit SAP Luigi

Um jetzt Micro Frontends in SAP Luigi einzubinden, wird dessen Adresse in der Luigi-Config.js angegeben. SAP Luigi erkennt diese und generiert die Navigation, mit welcher man das Frontend aufrufen kann. Für die Integration der Micro Frontends verwendet SAP Luigi iFrames. Die Adresse des Micro Frontends, das unter dem definierten Navigations-Knoten erreichbar ist, wird immer unter der „viewUrl“ definiert. In dem hier gezeigten Beispiel ist das von SAP Luigi generierte Micro Frontend „Home“ als Startseite gesetzt.

Dieses Micro Frontend ist auf derselben Instanz wie SAP Luigi gehostet. Diese muss nicht zwingend verwendet werden, lässt sich aber nutzen, um sich beispielsweise in SAP Luigi und in die Micro Frontend Architektur einzuarbeiten, ohne einen extra Host aufmachen zu müssen. SAP Luigi lässt sich mit verschiedenen Frameworks für das auf dem selben Host laufende Micro Frontend initiieren. Optionen für diese sind unter anderem Vue.js, Angular, React oder auch SAP UI5. Es steht auch die Option offen, normales JavaScript zu verwenden oder sonstige Frameworks. Dafür muss dann selbst Hand angelegt werden.

In dem hier gegebenen Beispiel wird noch ein weiteres Micro Frontend angebunden, dass für Demonstrationszwecke auf „localhost:5001“ läuft. So wird das Micro Frontend-Konzept richtig umgesetzt und ein anderer Host in die Anwendung eingebunden.

Kommunikation mit Frontend

Um mit dem Micro Frontend kommunizieren zu können, lässt sich Luigi unter dem Feld „communication“ auf der Root-Ebene der Luigi-Config mit Methoden versehen, die auf Events reagieren können. So wird durch das „postMessage“-System der iFrames eine ID an das Micro Frontend vom Orchestrator oder andersrum geschickt und die entsprechende Anwendung reagiert mit der registrierten Methode. Im untenstehenden Beispiel löst das Event „greet-orchestrator“ das Event „greet-from-orchestrator“ beim Micro Frontend aus und zusätzlich erfolgt die Sendung einer Broadcast-Nachricht an alle offenen Micro Frontends.

Konfiguration der kommunikation des Micro Frontends mit dem Orchestrator via SAP Luigi

Das Event gibt darüber hinaus noch weitere Informationen mit. Zuerst wird das Message-Objekt „customMessage“ als JS-Objekt übergeben, das vom Micro Frontend definierte Inhalte sendet. Unter „microFrontend“ werden Informationen über das auslösende Micro Frontend, wie beispielsweise dessen „id“, übergeben, die sich dann nutzen lässt, um diesem spezifischen Micro Frontend eine Nachricht zu senden. Mit „navNode“ lässt sich die Information über den Navigationspunkt des Micro Frontends an den Orchestrator übergeben.

Kommunikation mit Luigi-Core

Im Micro Frontend wird die Luigi-Client-Bibliothek installiert, um mit dem Luigi-Core kommunizieren zu können. Dafür werden mit der Methode „addCustomMessageListener“ zwei Events auf die Identifikatoren „greet-from-orchestrator“ und „orchestrator-broadcast“ registriert. Um weitere Funktionen von SAP Luigi zu demonstrieren, werden hier die „showAlert“-Funktionen verwendet, um einige UX/UI-Funktionen von Luigi zu nutzen. Diese lösen im Orchestrator eine Alert-Box aus, mit den an das Micro Frontend gesendeten Daten als Nachricht.
Damit das Micro Frontend weiß, dass es in einer Luigi-Anwendung aufgerufen wird, wird mit der Methode „addInitListener“ eine Methode auf den Fall registriert, dass das Micro Frontend in einer Luigi-Core Anwendung aufgerufen wird. In diesem Fall speichert sich das Micro Frontend diese Information mit einem Boolean und es wird „sendCustomMessage“ verwendet, um das im Orchestrator registrierte Event „greet-orchestrator“ auszulösen.

Integration SAP LuigiClient in Micro Frontend

Die Kommunikation kann für verschiedenste Szenarien genutzt werden. Eines davon wäre zum Beispiel ein Handshake von Orchestrator mit Micro Frontend, um zu prüfen, ob ein verifiziertes Micro Frontend vorliegt. So lässt sich die Sicherheit der gesamten Anwendung steigern.
SAP Luigi bietet so eine solide Basis, um vor allem Anwendungen innerhalb einer großen Anwendung zusammenzuführen und sicherzustellen, das alle nötigen Tools an einem Ort sind.

Micro Frontends mit Module Federation

Als Alternative zu SAP Luigi besteht auch die Möglichkeit, Module Federation zu nutzen – was aber einen etwas anderen Ansatz verfolgt: wenige, aber dafür mächtige Funktionen.

Module Federation bietet eine Möglichkeit, Code mit anderen Anwendungen zu teilen. So lässt sich ein JS-Modul durch Parametrisierung einfach teilen und auch einfach referenzieren. Bei einer solchen Nutzung gibt es jedoch verschiedene Dinge zu beachten:

Zuerst gilt es, einen Ausfallschutz zu integrieren, da ein Micro Frontend nicht garantiert verfügbar für den Orchestrator ist. Wie man das umsetzt, sei es durch Caching oder einfaches Error-Handling, steht offen. Unbehandelt kann so ein Fehler die gesamte Anwendung zum Absturz bringen, da diese das verwendete Modul nicht finden kann.

Außerdem ist bei der Implementierung eines Micro Frontend in einem anderen Framework darauf zu achten, dass das Modul anders zu verwenden ist. Ein Svelte-Modul, das beispielsweise in eine React-Anwendung integriert wird, muss gezielt in eine React-Komponente eingebunden werden. Somit ist keine unbeschwerte Technologieagnostik gegeben.

Module Federation und Build-Tools

Wie wird jetzt aber so ein Modul geteilt? Man gibt dem verwendeten Build-Tool die entsprechenden Anweisungen. Hier wird die Module Federation in dem Build-Tool “Vite” umgesetzt. Dafür wird ein Plugin hinzugefügt, welches mit verschiedenen Informationen parametrisiert ist. Wichtig sind hier vor allem „filename“, was den Dateinamen des Einstiegpunktes des Micro Frontends definiert, „exposes“ welches die Module unter dem definierten Feldnamen verfügbar macht (hier ist myComponent.svelte unter myComponent veröffentlicht) und das „shared“-Attribut, unter welchem verwendete Bibliotheken gelisted werden. Dadurch, dass das Micro Frontend der konsumierenden Anwendung mitteilt, welche Bibliotheken sie verwendet, kann der Orchestrator schauen, ob er diese schon selbst installiert hat und so doppeltes Laden von Bibliotheken verhindern. Dies senkt vor allem die Netzwerk-Last, da weniger geladen werden muss.

Nutzung von Micro Frontends mit Module Federation

Orchestrator

Im Konsumenten, also im Orchestrator sieht das ganze sehr ähnlich aus, mit dem Unterschied, dass hier nicht „exposes“ verwendet wird, sondern „remotes“. Hier werden die Micro Frontends referenziert. Das Feld „ComponentProvider“, welches auf den Einstiegspunkt des Micro Frontends verweist, enthält alle Komponenten des Micro Frontends.

„ComponentProvider“ kann dann auch wie eine Bibliothek im Code verwendet werden.

Da es sich hier um eine Svelte-Komponente handelt, die in React integriert wird, muss die Svelte-Komponente noch explizit in React nutzbar gemacht werden. Nach einem Tutorial von Jack Herrington, kann also beispielsweise eine Funktion erstellt werden, die Svelte-Komponenten in eine React-Komponente einbinden und diese dann zurückgeben. So wird durch die Funktion SvelteWraper bewiesen, dass eine gewisse Technologieagnostik umsetzbar ist. Wie man andere Frameworks zusammenbringt und inwieweit das sinnvoll ist, bleibt herauszufinden, ist aber nicht unmöglich. Svelte in React funktioniert.

Jetzt wissen wir, was Micro Frontends können. Und wir kennen zwei Arten, wie wir diese integrieren. Nun stellt sich nur noch die Frage, wie man die Umsetzung plant, wie man am besten vorgeht. Schauen wir auf die Methoden.

Agile Methoden

Eine moderne Webanwendung, die Micro Frontends implementiert, sollte nicht unbedingt nach dem Wasserfallmodell entwickelt werden. Die Methode ist nicht flexibel genug. Mit agilen Methoden wie Test Driven Development (TDD), Behaviour Driven Development (BDD) und Domain Driven Design (DDD) bieten sich uns einige Alternativen, auf die wir noch kurz eingehen.

TDD

TDD ist auf einer sehr technischen Ebene angesiedelt. Hier werden vor allem genaue Funktionen geprüft. In der Micro-Frontend-Entwicklung kann also vorher für ein zu integrierendes Micro Frontend ein Test definiert werden, der die Komponente komplett durchtestet. Da TDD genau auf die Funktionalität eingeht, sollte die zu testende Komponente nicht allzu komplex sein. So ergibt es aus der Sicht des Orchestrators folglich Sinn, TDD vor allem für sehr kleine Micro Frontends zu verwenden.

BDD

Wenn ein Micro Frontend entwickelt wird, dass mehrere Aufgaben löst und nicht allzu komplex ist, dann kann BDD der richtige Ansatz sein. Im BDD liegt der Fokus darauf, das Verhalten der Komponenten präzise zu definieren. Das beinhaltet eine umfassende und genaue Beschreibung aller Verhaltensweisen, die in der Interaktion mit der Komponente auftreten können. Dieser Ansatz ist aus der Sicht des Orchestrators für die Micro Frontends deshalb praktisch, da dadurch die Definition des Verhaltens als Anforderungen an das Micro Frontend gestellt werden. So lassen sich also Micro Frontends als Features definieren und integrieren.

Der Unterschied zum TDD liegt darin, dass nicht mehr einzelne Funktionen isoliert getestet werden, sondern Verhaltensweisen des Features in bestimmten Nutzungsszenarien. Das bedeutet, dass so die Zusammenarbeit der Funktionen geprüft wird. Die Komplexität des Micro Frontends und die Technologie, die dafür verwendet wird, ist für den Orchestrator irrelevant und liegt im Verantwortungsbereich des zuständigen Entwicklerteams. Da die Anforderungen an die Micro Frontends nicht technologieabhängig, sondern durch die Verhaltensweise definiert sind, verstehen auch alle Beteiligten die Anforderungen an das Feature und können darüber diskutieren. Dies fördert im Allgemeinen die Qualität des Features.

DDD

DDD verfolgt die Idee, dass Definitionen für unterschiedliche Teile des Systems klar abgegrenzt ihre Bedeutung tragen. Das lässt sich auf Micro Frontends in dem Sinne übertragen, dass jedes Micro Frontend ein System mit einem eigenen Kontext ist, was wiederum bedeutet, dass sich durch die Funktionalitäten und Verantwortlichkeiten auch eine eindeutige Kontextabgrenzung ergibt. Daraus resultiert auch, dass eine einheitliche Sprache für jedes Micro Frontend definiert wird, um so die Kommunikation innerhalb eines Teams, zwischen mehreren Teams, die an dem Micro Frontend arbeiten, und zwischen diesen und den Domainexperten eindeutig zu halten und zu erleichtern. Es entsteht aber auch ein erheblicher Mehraufwand, der sich für einfache Frontends und Anwendungen als überzogen darstellt.

Micro Frontends – ein Fazit

Zusammenfassend lässt sich also sagen, dass Micro Frontends viel bieten, um Anwendungen pflegbar zu halten. Dadurch, dass aber durch jeden neuen Micro Frontend Host ein gewisser Extraaufwand entsteht, bleibt immer abzuwägen, ob es sich lohnt, ein neues Micro Frontend für das geforderte Feature zu implementieren. Das kann von der Komplexität, der Technologie oder auch den Zukunftsplänen des zu integrierenden Features abhängig gemacht werden. Bei sehr großen Webanwendungen überwiegen die Vorteile die Nachteile aber deutlich.

Tags