Wir verwenden Cookies, um dir das optimale Nutzererlebnis bieten zu können. Gerne kannst du diese unter Cookie-Einstellungen anpassen. Unsere Datenschutzbestimmungen findest du hier.

April 26, 2022

Micro-Frontends - Microservices fürs Web

January 4, 2022

Micro-Frontends - Microservices fürs Web

In der Software-Architektur gibt es unterschiedlichste Modelle, die sich bei Applikationen bewährt haben.

Von Monolithen über SOA (Service Orientierte Architektur), Microservices bis hin zu Serverless Architekturen gibt es die unterschiedlichsten Lösungen für die Entwicklung von neuen Produkten. Dieser Beitrag befasst sich mit einem neuen (alten) Lösungsansatz und spinnt den Gedanken der Microservice-Architekturen weiter: Micro-Frontends!

Wettbewerbsvorteil durch Architektur

Die meisten Projekte starten mit monolithischen Architekturen, da die Entwicklerteams eine überschaubare Größe haben. Sobald jedoch die Nutzerzahlen steigen, muss die Software mitskalieren. Die besten Beispiel dafür sind Big-Player im E-Commerce Bereich wie z.B.: Amazon oder Zalando. Hier arbeiten unzählige Teams an der Software und setzen auf Microservices- und Serverless-Architekturen. Damit Features so schnell wie möglich entwickelt werden können, wird versucht, die Abhängigkeiten zwischen den Teams auf ein Minimum zu reduzieren. Denn je schneller Features in Produktion deployed werden können, desto größer der Wettbewerbsvorteil für die Unternehmen und desto mehr Umsatz kann generiert werden. Des Weiteren schafft man dadurch die Möglichkeit, dass je Team spezifische Technologien, wie React, Angular, Go, Rust oder auch Kotlin, eingesetzt werden können, um bestimmte Aufgabenstellungen am effizientesten zu lösen. Zusätzlich wird die Einarbeitungszeit neuer Kolleg*innen durch den kleineren Umfang der Services deutlich reduziert, da die Komplexität wesentlich geringer ist.

Genau diesen Vorteile möchte man nun auch im Frontend ausnutzen. Die Backend -Architektur hat sich über die Jahre immer weiterentwickelt und ist immer feingranularer geworden. Jedoch wird im Frontend immer noch ein Monolith entwickelt und auch so ausgeliefert, was den Abstimmungsaufwand erhöht und die Entwicklungszeit für neue Features unnötig verlängert. Im Jahr 2016 kam bei Thoughtworks am Technology Radar erstmals der Begriff und die Idee der “Micro-Frontends” auf, die dieses Problem in Angriff nehmen sollte. Sie spinnen den Gedanken der Microservices weiter und unterteilen auch Webapplikationen in Microservices.

In Abbildung 1.1 sieht man, die Unterschiede von Monolithen bis hin zu dem neuesten Ansatz der Micro-Frontends.

Abbildung 1.1- Unterschiede von Monolith zu Micro-Frontends (Quelle: https://miro.medium.com/max/724/1*rhck0WCVgO26Y5ynslwdEg.png)

Die wesentliche Idee bei Micro-Frontends ist es das, die Webapplikation ganz nach dem Ansatz von Domain Driven Design in ihre Fachlichkeiten unterteilt wird - Stichwort Bounded Context. Je Fachlichkeit gibt es ein Team, das eine Page-Ownership hat. Wichtig dabei ist es, dass die Teams die Ende-zu-Ende Entwicklung übernehmen und somit vom Datenbankdesign bis hin zum Deployment und Monitoring alle Aufgaben übernehmen (“You build it - You run it”). Wie relevant dabei vertikalen Teams sind, um keine organisatorische Unabhängigkeit zu erreichen, wird in Abbildung 1.2 veranschaulicht.

Abbildung 1.2- Die Idee autonome Teams zu bilden, steht bei Micro-Frontends im Vordergrund. (Quelle:https://martinfowler.com/articles/micro-frontends.html)

Prinzipiell wird bei Micro-Frontends zwischen den Begriffen Pages und Fragments unterschieden. Pages stellen dabei eine gesamte Seite dar wie zum Beispiel die Produktdetailseite. In dieser Seite könnte dann nicht nur die Details zum Produkt abgebildet sein, sondern auch Fragments von anderen Teams. Ein Beispiel wären das “Andere Kunden haben auch gekauft” - Element oder aber auch der “Kaufen-Button”, die von anderen Teams als Fragmente in die eigene Page integrieren werden können. 

Abbildung 1.3 zeigt die Page vom Team “Product”. Diese hat die Fragments in Form vom Warenkorb und den “Kaufen”-Button vom Team “Checkout”, sowie auch ein Produkt-Recommendation-Banner vom Team “Inspire” integriert.

Abbildung 1.3- Die Seite von Team “Product”, um die Idee von Micro-Frontends zu veranschaulichen. (Quelle: https://micro-frontends.org/)

Die Micro-Frontends werden dann unabhängig voneinander in der CI/CD Pipeline gebaut, getestet und dann in Produktion ausgeliefert. Die Herausforderung dabei ist es, dass die Applikationen am Ende so zusammengesetzt werden, dass es für den Endnutzer wirkt, als würde er eine Webseite besuchen, die als Einheit entwickelt wurde. Abbildung 1.4 zeigt eine vereinfachte Darstellung, wie diese Schritte aussehen.

Abbildung 1.4- Jedes Micro-Frontend besitzt eine eigene CI/CD Pipeline. (Quelle: https://martinfowler.com/articles/micro-frontends.html)

Damit die finale Webapplikation so aussieht, als wäre sie in einem Guss entwickelt worden, muss sich im Unternehmen auf Makro-Architekturebene geeinigt werden, welche Konventionen und Design - Guidelines einzuhalten sind. Damit wird sichergestellt, dass der Endnutzer ein einheitliches Look & Feel präsentiert bekommt. Eine weitere Herausforderung stellt auch die Versionierung der Services dar. Denn es muss sichergestellt sein, dass die verteilten Systeme miteinander kompatibel sind und kommunizieren können. Bei der Kommunikation selbst muss man sich zusätzlich Strategien zurechtlegen, wie man möglichste resiliente Applikationen bereitstellt, damit die Nachrichten korrekt verarbeitet werden, auch wenn Systeme teils nicht erreichbar sind, Netzwerkfehler auftreten oder mehrfach versendet werden.

Um diese Herausforderungen zu lösen, gibt es die drei essentiellen Säulen bei der Entwicklung von Micro-Frontends, die in Abbildung 1.5 dargestellt werden. Zuerst steht die Frage, wie man Seitenübergänge gestalten kann. Das Navigieren zwischen den einzelnen Pages muss nahtlos passieren und der Nutzer darf nicht den Eindruck bekommen, dass er sich zwischen verschiedenen Applikationen bewegt. Die verschiedenen Techniken, um diese Problemstellung zu lösen, fallen unter den Punkt: Routing.

Eine weitere Herausforderung stellt das Zusammenfügen der einzelnen Micro-Frontends zu einem großen Ganzen dar. Unter dem Begriff Composition findet man dazu Ansätze, die sich weiter in die Kategorien Client-und Server Side Composition.

Abschließend stellt sich die Frage, wie Pages und Fragments miteinander Nachrichten austauschen können - Communication. Nachgehend werde ich einige der gängigsten Lösungsansätze in diesen drei Themengebieten erläutern.

Abbildung 1.5- Die drei Säulen von Micro-Frontends. (Quelle: Michael Geers. Micro Frontends in Action. Manning Publications Co., 2020)

Routing

Beim Routing spielt vor allem der Übergang zwischen den verschiedenen Seiten eine wesentliche Rolle. Die Frage, wie kommt der Nutzer von der Seite eines Services zu der Seite eines anderen, ohne dass die Benutzererfahrung darunter leidet, ist hierbei essentiell. Der simpelste Ansatz ist der Einsatz von Hyperlinks. Eine weiter Möglichkeit stellt der clientseitige Einsatz von einer sogenannten Application Shell dar, die für die Übergänge zwischen den Seiten zuständig ist. Außerdem existieren auch serverseitige Ansätze, wie der Einsatz eines Proxyservers, der das Routing übernimmt.

Hyperlinks

Der Einsatz von Hyperlinks ist eine der simpelsten Techniken, um den Benutzer von einer Seite zur nächsten zu führen. Dabei entsteht ein Vertrag zwischen den Services. Diese müssen die jeweiligen Links zu ihren Seiten mit den anderen Teams kommuniziert. Die Teams müssen somit für eine Integration lediglich den entsprechenden Hyperlink in ihre eigene Seite integrieren, um den Übergang zu ermöglichen.

Nginx

Mit einem Nginx-Server kann man serverseitiges Routing umsetzen. Dabei wird der Nginx-Server als Reverse-Proxy eingesetzt. Im Gegensatz zu der Variante mit den Hyperlinks muss nur eine einzige Adresse bekannt sein. In der URL kann zum Beispiel der Teamname oder ein domänenspezifischer Begriff stehen. Die Verwendung eines Reverse-Proxy bringt nicht nur den Vorteil, dass alle verteilten Applikationen nur unter einer Adresse angesprochen werden können. Zusätzlich dazu wird auch die Performance verbessert, da nur mehr ein einziger DNS-Lookup notwendig ist. Des Weiteren unterliegen CSS- und JavaScript-Dateien der Same-Origin-Policy. Der Mehraufwand sich mit CORS auseinanderzusetzen entfällt durch den Einsatz des Reverse-Proxy.

Application Shell

Serverseitiges Routing stellt eine Möglichkeit der Weiterleitung dar. Allerdings stößt diese Technik an ihre Grenzen, wenn es um Single Page Applications geht. Setzt ein Team ein SPA-Framework ein, so besteht der große Vorteil, dass die Applikation aus einem HTML-Dokument besteht, das vom Browser geladen wird. Danach können Teile, die sich ändern, dynamisch ausgetauscht werden, ohne dass eine weitere Anfrage an den Server gestellt werden muss. Wird jedoch auf die SPA-Framework Seite eines anderen Services weitergeleitet, muss trotzdem die gesamte HTML-Seite durch den Browser geladen werden. Damit derartiges Nachladen verhindert wird, gibt es den Ansatz eine Application Shell, um die Micro-Applikationen zu spannen. In Abbildung 1.6 findet sich die Darstellung dieses Vorgehens. Innerhalb der Shell befinden sich die Micro-Apps, die bei Bedarf geladen werden. Die Übergänge zwischen den Teilen der unterschiedlichen Anwendungen senden keine erneute Anfrage an den Server. Diese Technik ermöglicht es, dass Teams ihre Micro-Frontends als Single Page Applications implementieren können. Durch die Verwendung einer Application Shell behält man die Vorteile, die Single Page Applications mit sich bringen.

Abbildung 1.6 - Aufbau einer Application Shell. (Quelle: https://www.angulararchitects.io/aktuelles/6-steps-to-your-angular-based-mic rofrontend-shell)

Serverseitige Komposition

Micro-Frontends sind besonders im E-Commerce Bereich verbreitet. Bekannte Vorreiter dieser Architektur sind Amazon, Ikea und Zalando. Für die Unternehmen ist es von besonderer Bedeutung, dass die Ladezeiten auf ein Minimum reduziert werden, damit die Nutzer ein optimales Einkaufserlebnis haben. Dies wird in Abbildung 1.7 verdeutlicht. Vor allem muss bedacht werden, dass nicht alle Nutzerinnen und Nutzer immer Zugang zu Glasfaserleitungen und Highspeed-Internet haben. Des Weiteren möchte man vermeiden, dass Kunden aufgrund schlechter Benutzererfahrung zu Anbietern mit signifikantem Performancevorteil wechseln.

Die Vorteile hinsichtlich der Performance können aufgrund korrekter Architekturentscheidungen erreicht werden. Bei der serverseitigen Komposition befindet sich ein Kompositionsserver zwischen den Clients und den Applikationen. Dieser ist für die Zusammensetzung zuständig. Ein Client sendet dazu eine Anfrage an den Server. Der wiederum fordert die Seiten und Fragmente bei den Applikationen an und baut daraus ein HTML-Dokument zusammen. Das zusammengesetzte HTML-Dokument wird dann an den Client ausgeliefert. Damit werden vor allem die Ladezeiten im Vergleich zur clientseitigen Komposition wesentlich verbessert. Für serverseitige Komposition gibt es unterschiedliche Technologien, die eingesetzt werden können. Ältere Spezifikationen wie SSI, das für Server Side Includes steht oder ESI, das ein Akronym für Edge Side Includes ist, eignen sich ebenso wie Open-Source Lösungen. Unter diesen Frameworks sind zum unter anderem Podium und Tailor vertreten.

Abbildung 1.7 - Ladezeiten beeinflussen Download- und Verkaufszahlen signifikant. (Quelle: https://neilpatel.com/blog/ecommerce-conversion-hacks/)

SSI Server Side Includes

Server Side Includes sind eine etwas ältere aus den 1990er Jahren stammende Technologie. SSI wurde bereits damals dazu verwendet, Direktiven in HTML-Dokumente einzubinden, die dann dynamisch ausgetauscht wurden. Mittels Include-Anweisung konnte unter Verwendung eines Apache Webservers und SSI Header und Footer eingebunden werden. Des Weiteren ist es mittels SSI auch möglich, Skripte auszuführen und ganze Dateien zu inkludieren. Zusätzlich bieten SSI Konditionale Ausdrücke, Variablendeklaration und die Ausführung von Programmcode mittels execute-Statement an. Moderne Webserver wie Nginx (https://www.nginx.com/) bieten Unterstützung für SSI an. Dabei muss am Server das SSI-Feature in der Konfiguration aktiviert werden. Mittels der Include-Direktive kann dann angegeben werden, welches Fragment auf der Seite eingebunden werden soll. In Abbildung 1.8 wird gezeigt, wie der. Client im ersten Schritt die Anfrage an den Server stellt. Der Webserver leitet anhand der definierten Routen an die dahinter liegende Applikation weiter. Diese stellt ein HTML-Dokument als Antwort zur Verfügung. In diesem HTML-Dokument befinden sich die SSI-Anweisungen. Der Webserver übersetzt die Direktive und stellt erneut die Anfrage an den in der Anweisung spezifizierten Applikation-Server, der das Fragment für die Seite anbietet. Sobald die Antwort des Fragment-Servers erhalten wird, setzt der Server das Markup in die Seite ein. Sind alle SSI-Direktiven substituiert worden, so wird das fertig zusammengebaute HTML-Dokument als Antwort an den Client gesendet.

Abbildung 1.8 - Funktionsweise von Server Side Includes mittels Nginx. 

ESI Server Side Includes 

Eine Alternative zu den Server Side Includes stellen die sogenannten Edge Side Includes dar. ESI sind ähnlich wie SSI Direktiven. ESI werden mittels XML-Markup definiert. Genauso wie SSI haben ESI zum Ziel, dass verschiedene Ressourcen effizient in ein Dokument eingebunden werden. Allerdings ist ein wesentlicher Unterschied, dass ESI vor allem auf Chaching setzt. Im Vergleich zu SSI bietet bei ESI der Include-Tag zusätzliche Attribute wie „alt“ an. Dieses dient dazu, dass Fallbacks definiert werden können, sollte die Ressource nicht eingebunden werden können. Ein HTTP-Server, der als Proxy-Server fungiert, ESI unterstützt und auf Caching ausgelegt ist, ist zum Beispiel Varnish (https://varnish-cache.org/).

Tailor

(https://github.com/zalando/tailor)

Zalando hat Schritt für Schritt die monolithischen Strukturen im Frontend ihrer Applikation entfernt. Beim Erstellen des internen Frameworks für Micro-Frontends entstanden dabei einige Services. Diese sind teilweise als Open-Source-Lösungen veröffentlicht worden. Das interne Vorgehen wurde auf den Namen „Project Mosaic“ getauft. (https://www.mosaic9.org/)

Project Mosaic enthält unter anderem einen Routing Service namens Innkeeper, der für die Routenfindung zuständig ist. Innkeeper steht unter der Apache Lizenz auf Github zur Verfügung (https://github.com/zalando/innkeeper). Der Service ist in Scala geschrieben und stellt die Routeninformationen in einem eskip-Dateiformat zur Verfügung. Der Innkeeper-Service ist so entworfen worden, dass er automatisch nach dem Start versucht, sich zu der Applikation namens Skipper zu verbinden. 

Skipper ist ein HTTP-Reverse-Proxy Server, der in Go geschrieben wurde. Das Entwicklungsteam von Zalando gibt an, dass Skipper über 300.000 Routen-Look-ups bewältigen kann. Skipper wird ebenfalls auf Github unter der Apache Version 2.0 Lizenz angeboten (https://github.com/zalando/skipper).

Ein weiterer wichtiger Baustein im Project Mosaic ist Tailor. Beeinflusst wurde Tailor von Facebooks „BigPipe“. Da dieser Ansatz allerdings ungeeignet für SEO und die Anforderung an E-Commerce-Shops war, wurden eigene Services entwickelt. Tailor stellt dabei Middleware dar, die in jeden Node.js Server eingebunden werden kann. Der Service, geschrieben in JavaScript, wird unter der MIT-Lizenz auf Github angeboten. Tailor dient dabei als Layout-Service und setzt mehrere Fragmente serverseitig zusammen, bevor ein fertig zusammengesetztes HTML-Dokument an den Client ausgeliefert wird. Zusätzlich werden Scripte und Stylesheets für die zugehörigen Fragmente eingebunden. Die Information für die CSS- und JavaScript-Dateien werden via HTTPHeader vom Fragmentserver an den Kompositionsserver übermittelt. 

Implementierungstechnisch setzt Tailor auf die non-blocking I/O von Node.js und implementiert zusätzlich drei eigene Streams. Einer der drei Streams ist der ParserStream. Dieser baut auf dem Konzept des SAX-Parsers auf, um die Fragment-Tags zu verarbeiten. Als weitere Implementierung dient der Stringifier-Stream. Er setzt an die markierten Stellen das Markup der Fragmente ein. Als letztes ist die Async-Stream Implementierung zu nennen. Diese wird dazu genutzt, um asynchron deklarierte Elemente zu verarbeiten. 

Eine Stärke von Tailor ist die Fehlertoleranz, die durch Fallbacks erreicht wird. Sollte ein Fragmentserver nicht oder zu spät eine Antwort an Tailor senden, liefert der LayoutService trotzdem das Markup aus. Des Weiteren werden rasche Initialladezeiten, die als Time To First Byte (TTFB) bezeichnet werden, erreicht. Dies ist möglich, da Tailor die Fragmente asynchron als Streams verarbeitet und in das Markup integriert. 

Gute TTFB-Werte werden dadurch erreicht, da Tailor die Fragmente, die im sichtbaren Bereich sind, priorisiert und schnellstmöglich an den Client via HTTP streamt. Die Fragmente, die nicht beim initialen Seitenaufbau im sichtbaren Bereich liegen, sollten vom Entwickler oder der Entwicklerin als asynchron deklariert werden. Dadurch wird ein Nachladen erreicht, sobald die sichtbaren Bereiche ausgeliefert wurden. 

In Abbildung 1.9 wird die beschrieben Arbeitsweise des Streamings dargestellt. Dabei ist anzumerken, dass die Darstellung stark vereinfacht ist und in der Praxis weitere Faktoren wie die Bandbreite und Latenzen eine wesentliche Rolle spielen. Zusätzlich geht dieses Modell von einer linearen Abarbeitung aus, was von der Realität abweichen kann. In diesem fiktiven Beispiel existiert eine Seite (in weiß dargestellt), die zwei Fragmente A (blau) und B (orange) beinhaltet. Direkt danach wird das im sichtbaren Bereich liegende Fragment A bereits an den Client gestreamt, damit der Nutzer auch den Content beim initialen Laden der Seite sieht. Anschließend wird Fragment B mit niedrigerer Priorität ausgeliefert, da dieses im nicht-sichtbaren Bereich liegt und der Nutzer dieses erst beim Scrollen sehen würde. Zusammenfassend bedeutet das, dass frühestmöglich nach dem Eintreffen des initialen Request des Clients mit dem Ausliefern des Markups begonnen wird. Der Browser kann direkt mit dem Parsen bzw. Rendern nach Erhalt der ersten Daten beginnen und somit werden dem Nutzer rasch die Inhalte der Seite dargestellt. Dies wiederum ist essentiell, damit Nutzer die Webseite nicht vorzeitig verlassen.

Abbildung 1.9 - Streaming-Response von Tailor. (Quelle: Michael Geers. Micro Frontends in Action. Manning Publications Co., 2020)

Podium

(https://podium-lib.io/)

Podium ist eine Node.js Bibliothek für Micro-Frontends. Podium wurde von dem Unternehmen FINN, das in Norwegen eines der größten Kleinanzeigenportale ist, entwickelt. 

Die Node.js Bibliothek steht auf Github unter der MIT-Lizenz zur Verfügung. Die einzelnen Fragmente, die in Seiten enthalten sein können, werden als Podlets bezeichnet. Für Seiten, die diese Podlets einbinden, wird der Begriff Layout verwendet. 

Abbildung 1.10 zeigt die Instanzierung eines Podlets mit einem Manifest. Das Manifest ist eine JSON-Datei, die unter der Route „/manifest.json“ abrufbar ist. In dem Manifest wird der Name des Fragments definiert. Des Weiteren findet man einen Verweis auf den HTTP-Endpunkt für den Inhalt des Fragments. Kann der Inhalt nicht geladen werden, so besteht die Möglichkeit unter „fallback“ eine Alternative für den Fehlerfall anzugeben. Neben dem Namen ist es auch möglich, eine Versionsnummer für das Podlet zu vergeben. Zusätzlich können auch Script-Dateien und Stylesheets eingebunden werden.

Abbildung 1.10 - Podium in Kombination mit Express und Node.js. 

Abbildung 1.11 zeigt die Erzeugung eines Layouts und das Einbinden der Podlets für eine Seite. Das Layout ist nicht nur zuständig für die Auslieferung des vollständig zusammengefügten HTML-Dokuments. Es sendet via HTTP-Header an die Podlets Kontextinformationen, was den Podlets die Flexibilität gibt, dynamisch Content zu generieren, um sich an die Anforderungen von spezifischen Layouts anzupassen.

Abbildung 1.11 -  Komposition am Layout-Server mit Podium. 

Clientseitige Komposition

Serverseitige Komposition kommt zum Einsatz, wenn es auf schnelle initiale Ladezeiten ankommt. Wenn Benutzer Einstellungen auf der Seite ändern oder Filteroptionen in einer Liste auswählen, wird beim serverseitigen Ansatz eine erneute Anfrage an den Server gesendet. Für die Benutzerfreundlichkeit einer Seite ist es jedoch essentiell, dass Seiten nicht jedes mal erneut geladen, sondern nur die geänderten Teile dynamisch ausgetauscht werden. Dies kann man mit Single Page Application wie Angular, React oder Vue.js erreichen. Ein SPA-Framework eignet sich nicht, um mit dessen Komponenten eine Micro-Frontend Architektur aufzubauen. Der Nachteil wäre, die Abhängigkeiten, die alle Services zu der verwendeten SPA haben. Bei Änderungen wären somit alle Services betroffen. Um dies zu vermeiden, soll die Unabhängigkeit erhalten bleiben und jedes Team ihre Applikation so isoliert wie möglich ausliefern können. Für technisch simple Applikationen bieten sich iframes an. Eine weitere Alternative ist die clientseitige Komposition mit AJAX. Eine moderne Möglichkeit mit guter Isolation bieten Webcomponents.

iframes

Eine der ältesten und simpelsten Lösungsansätze für clientseitige Komposition stellen iframes dar. Der iframe-Tag erhält via dem Attribute „src“ die Quelle, die eingebettet werden soll. Mittels Höhen- und Breitenangabe kann das iframe in der korrekten Größe von dem Team, das die HTML-Seite entwirft, eingebunden werden. Dabei wird für jedes eingebundene iframe ein eigener Browsing Context erzeugt. Auch globale Variablen des iframe-Kontexts sind nicht auf der Seite verfügbar. Die Kommunikation zwischen Seite und Fragment ist durch die Isolation, die mit iframes erreicht wird, stark eingeschränkt. Eine Option stellt ein Message Bus dar, damit Informationen zwischen Seite und iframe-Fragment ausgetauscht werden können. Der Einsatz von iframes für clientseitige Komposition von Micro-Frontends wurde von Unternehmen wie Spotify bereits im Jahr 2012 betrieben. Spotify hat dabei einzelne Fragmente in ihrem Webplayer als iframes integriert und konnte so ihre Teams unabhängig voneinander entwickeln lassen, ohne dass sich die freigegebenen Features gegenseitig beeinflussten.

AJAX

Eine weitere Variante für clientseitige Komposition stellt AJAX dar. Das Akronym AJAX steht für Asynchronous JavaScript and XML und wurde im Jahr 2005 von Jesse James Garret geprägt. AJAX ist per se keine Technologie, sondern eine Technik, um die Benutzererfahrung zu verbessern. Dabei wird der sich ändernde Inhalt dynamisch ausgetauscht. Zusätzlich können Validierungen am Client realisiert werden, ohne eine Anfrage an den Server stellen zu müssen. Den Kern bildet dabei das XMLHttpRequest Objekt, mithilfe dessen Daten asynchron empfangen werden können. Die Idee von AJAX ist es, dass nicht der Client mit dem Server interagiert, sondern eine Zwischenschicht eingezogen wird. Diese Schicht bildet die AJAX Engine, welche die Kommunikation mit dem Server übernimmt und so dem Client eine durchgehende Interaktion mit der UI gestattet. Ähnlich wie beim iframe-Ansatz ist es möglich, die Fragmente von anderen Services in seine Seite mit Hilfe von AJAX zu integrieren. Dabei muss die URL zum Fragment für die anderen Services zugänglich gemacht werden. Damit kann jedes Entwicklungsteam das Fragment beliebig in das eigene HTML-Dokument integrieren. Das Fragment beinhaltet dabei bereits Links zu den Stylesheets. Zu beachten ist, dass der AJAX Ansatz keine Isolation wie iframes bietet, da der Inhalt nahtlos in das HTML-Dokument der Hauptseite integriert wird.

Webcomponents

Webcomponents sind ein moderner Ansatz, um clientseitige Komposition umzusetzen. Sie setzen sich hauptsächlich aus drei Spezifikationen zusammen, die es ermöglichen einzelne HTML-Fragmente zu erstellen. Genutzt werden HTML-Templates, das ShadowDOM und Custom Elements, um dies zu ermöglichen. (https://www.webcomponents.org/)

Die Custom Elements ermöglichen die Erstellung von eigenen HTML-Tags. Diese müssen aus mindestens zwei Wörtern, die durch einen Bindestrich getrennt sind, bestehen. Tags, die nur aus einem einzelnen Wort bestehen, sind für das World Wide Web Consortium reserviert.

Es können Attribute wie bei nativen HTML-Tags implementiert werden, indem Getter und Setter als Wrapper verwendet werden. Innerhalb der Getter und Setter wird dann die „getAttribute“ oder „setAttribute“-Funktion aufgerufen. Der einzige Nachteil dabei ist, dass die Werte als Zeichenkette gespeichert werden.

Zusätzlich zu den Attributen bieten Custom Elements auch einen internen Lebenszyklus an. Dabei wird beim Erstellen eines Elements die Funktion „connectedCallback“ aufgerufen. Diese eignet sich zum Erstellen von Markup oder auch registrieren von Event-Handlern. Damit es zu keinen Problemen bei der Speicherfreigabe kommt, können beim Zerstören eines Custom Elements mit der Funktion „disconnectedCallback“ die verwendeten Ressourcen wieder freigeben werden. Die Funktion „attributeChangedCallback“ kann genutzt werden, um auf Änderungen an Attributen zu reagieren.

Ursprünglich war in HTML5 für Style-Tags das Attribut „scoped“ vorgesehen. Durch dieses war es einerseits möglich das Aussehen von gewissen Markup-Blöcken zu isolieren. Andererseits konnte man CSS-IDs und Klassen bedenkenlos verwenden, ohne dass die Definitionen Seiteneffekte bei anderen Elementen auslösten. Allerdings wurde das „scoped“-Attribut im August 2017 wieder von der Spezifikation entfernt. Auch die Browserhersteller haben die Spezifikation entfernt. Trotzdem haben Javascript Frameworks wie Vue.js ihre eigene Implementierung für die Funktionsweise von „scoped“ implementiert.

Damit die Isolation von CSS auch im w3c-Standard verfügbar ist, gibt es allerdings eine Alternative - den Shadow DOM. Ein HTML-Element hat mit der Funktion „attachShadow“ die Möglichkeit, einen Shadow DOM an sich selbst anzuhängen. Dadurch erreicht man einen hohen Isolationsgrad der generierten Komponente und läuft nicht Gefahr, dass Styledefinitionen nach außen dringen und Seiteneffekte nach sich ziehen. Das Element, das den sogenannten Shadow Tree unter sich einbindet, nennt sich Shadow Host.

Neben dem Shadow DOM taucht auch der Begriff Light DOM auf. Light DOM steht für das Markup, das vom Entwickler, der von Dritten entwickelte Webcomponents einbindet, geschrieben wurde. Der Light DOM ist insbesondere relevant, da Elemente aus diesem in den Shadow DOM mittels Komposition eingebunden werden können. Dies ist durch den Slot-Tag möglich. Ein Slot fungiert dabei als Platzhalter für Elemente aus dem Light DOM.

Damit es möglich ist, Markup einmal zu definieren und bei Bedarf wiederzuverwenden, gibt es die Spezifikation der HTML-Templates. Es werden nicht alle Elemente, die innerhalb dieser Templates definiert sind, gerendert. Laut Spezifikation repräsentiert der Template-Tag nichts und umfasst auch keine Kindelemente. Die Elemente werden in einem „DocumentFragment“ ohne Browsing Context abgelegt. Damit ist es Nutzer möglich, die Templates via Javascript zu selektieren und den Inhalt wiederzuverwenden.

Kommunikation

Seite-zu-Seite Kommunikation

Bei Micro-Frontends wird zwischen Seiten und Fragmenten unterschieden. Wenn zwei Seiten von unterschiedlichen Services miteinander kommunizieren müssen, kann das via URL und Queryparameter gelöst werden.

Seite-zu-Fragment Kommunikation

Damit eine Seite mit einem Fragment kommunizieren kann, können die Attribute von Webcomponents verwendet werden. Wenn sich der Wert des Attributes ändert, kann die Webcomponent mit dem „attributeChangedCallback“ darauf reagieren und sich daraufhin neu rendern.

Fragment-zu-Seite Kommunikation

Der andere Weg ist, wenn ein Fragment der umschließenden Seite Daten zukommen lassen möchte. Damit die Änderung auch höher liegenden Schichten bekannt wird, kann mit der CustomEvent-API gearbeitet werden.

Fragment-zu-Fragment Kommunikation

Die Kommunikation zwischen zwei Fragmenten auf einer Seite ist durch zwei Varianten möglich. Einerseits kann die Seiten-zu-Fragment und die Fragment-zu-Seite Kommunikation kombiniert werden. Dabei wird das Event von dem einen Fragment abgesendet und die Seite verarbeitet es. Die Seite leitet, sobald das Event auftritt, die Daten an das Attribut des anderen Fragments weiter. Diese kann sich selbst updaten, da es die Änderung des Attributwertes mitbekommt. Andererseits gibt es auch die Möglichkeit über die Broadcast Channel-API einen Message Bus zu implementieren, wie in Abbildung 1.12 dargestellt wird. 

Abbildung 1.12 -  Messagebus mittels BroadcastChannel-API. (Quelle: https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API)

Prototypen auf Github

In meiner Abschlussarbeit an der FH-Hagenberg habe ich mich intensiv mit dem Thema der Micro-Frontends auseinandergesetzt und anhand einiger Prototypen evaluiert, welche Vor- und Nachteile die einzelnen Vorgehensweisen haben. Für alle Interessierten steht das Repository öffentlich unter: https://github.com/EnvyIT/micro-frontend-demo zur Verfügung. Hier auch noch das Video in dem ich einen Vortrag über Microservices für Frontends beim Tech Talk von Devjobs.at halte.

Sie wollen Ihre Produkte online verkaufen und benötigen dazu einen individuellen Webshop?

Wir sind Ihr Digitalisierungspartner für Ihre individuellen E-Commerce Lösungen.

Mehr erfahren

Newsletter