Der Kleber macht’s – Microservices in einem Web-Projekt
Seite 2: Frontend-Integration und Micro-Frontends
Neben den theoretischen Architekturentscheidungen gibt es eine Reihe technischer Fragen zu lösen: Dabei ist eine wichtige Überlegung, Mechanismen zur Integration im Frontend und im Backend zu unterscheiden. Die im World Wide Web wohl am meisten verbreitete Methode zur Frontend-Integration sind Hyperlinks: Das integrierende Modul verweist mit einem HTML-a
-Tag auf eine URL, die das integrierte Modul zur Verfügung stellt. Beim Klick auf den Link, öffnet der Browser das hinter der URL liegende Dokument, hinter dem sich wiederum das Frontend des anderen Moduls verbirgt.
Auch die DatenĂĽbertragung via HTML-form
-Element ist eine bereits im HTML-Standard verankerte Möglichkeit, die eine Übertragung von Medientypen, außer reinem Text, erlaubt. Ein großer Vorteil der Frontendintegration über Hyperlinks oder HTML-Formulare liegt in den etablierten Standards und der daraus folgenden niedrigen Komplexität. Durch die direkte Nutzung der zugrundeliegenden Plattform kommt eine weitgehend Technologie-agnostische Integration und somit geringe Kopplung zustande.
Die Integration via Links erschlägt jedoch nicht jede Anforderung an Usability: Soll die Seite Inhalte aus der Verantwortlichkeit eines anderen Moduls anzeigen, um gegebenenfalls Kontext aus vorausgegangenen Schritten eines Prozesses zu liefern, reichen die Fähigkeiten von HTML-Links und -Formularen nicht mehr aus.
Micro-Frontends sind ein aktuell polarisierendes Thema für die Lösung dieser Probleme. Sie greifen die Idee der Self-contained Systems auf: Möglichst unabhängige Teams sollen komplexe, monolithische Systeme anhand klarer fachlicher Grenzen getrennt entwickeln. Jedes Modul stellt bestimmte, zu seinen fachlichen Anforderungen passende Frontend-Fragmente – auch Micro-Frontends genannt – zur Verfügung (siehe Abbildung 3). Andere Module können diese Micro-Frontends in ihrem Frontend integrieren. Die Einbindung dieser Fragmente sollte zugunsten loser Kopplung möglichst Technologie-agnostisch geschehen.
Es ist möglich, sie mit dem im HTML-Standard verankerten iframe
zu implementieren. Das ist aufgrund diverser Nachteile im responsiven Styling und ihrer eingeschränkten Kommunikationsmöglichkeiten nicht grundlos umstritten. Dennoch kann es manchmal sinnvoll sein, insbesondere aufgrund ihrer einfachen Schnittstelle, ihrer breiten Browserunterstützung und der Freiheit von zusätzlichen Abhängigkeiten in einigen Anwendungsfällen.
Transklusion ist eine mögliche Alternative zum iFrame. Hierbei rendert eine Seite ein von einem Fremdsystem zur Verfügung gestelltes HTML-Fragment client- oder serverseitig im eigenen DOM. Bei clientseitiger Transklusion bedienen Developer sich wieder eines Webstandards und ersetzen einen Hyperlink via AJAX durch das hinter der Ziel-URI befindliche HTML.
Diese Methode benötigt nur einen minimalen Anteil an JavaScript und eignet sich sehr gut für Progressive Enhancement: Alle grundlegenden Funktionen sind für alle Benutzerinnen und Benutzer unabhängig von Browserversion oder -einstellungen nutzbar, während erweiterte Funktionalität wie UX-Verbesserungen nur für modernere Systeme bereitgestellt werden.
Das folgende Listing zeigt, dass nur wenig JavaScript-Code notwendig ist, um Inhalte clientseitig zu transkludieren. Das prototypische Minimalbeispiel mithilfe des Web-Component-Standards zeigt, dass das Skript durch die Nutzung des a
-Tags innerhalb der Komponente anstatt einer via Parameter ĂĽbergebenen URL Progressive Enhancement unterstĂĽtzt.
class Transclude extends HTMLElement {
connectedCallback() {
const link = this.querySelector("a");
fetch(link.href)
.then(response => response.text())
.then(html => this.outerHTML = html);
}
}
customElements.define("transclude", Transclude);
FĂĽr serverseitige Transklusion kommen Techniken wie Server Side Includes (SSI) oder Edge Side Includes (ESI) zum Einsatz. Developer nutzen statt eines a
-Tags eine von der entsprechenden Technologie spezifizierte Markup-Language. Im Falle von ESI sieht das wie folgt aus:
<esi:include src="my-target-uri" alt="alt-target-uri" onerror="continue"/>
.
Zwischen Client und Server sitzt ein Reverse-Proxy, der das vom Server gelieferte Markup an den entsprechend gekennzeichneten Stellen mit den Zielinhalten anreichert, bevor er die Antwort an den Client liefert. Einige Web- und Application-Server wie der Apache HTTP Server oder Tomcat unterstĂĽtzen SSI bei entsprechender Konfiguration auch direkt.
Ein weiterer Mechanismus zur Integration von Modulen, der je nach Funktionsweise der Implementierung der Laufzeitintegration oder der Integration zur Entwicklungszeit zugeordnet wird, sind Rich Components. Das sind eigenständige JavaScript-Komponenten, die ein bestimmtes Subset an Geschäfts- oder Darstellungslogik enthalten und die Module für die Integration in andere Module zur Verfügung stellen. Für die Implementierung eignet sich wahlweise der mittlerweile sehr mächtige Web-Component-Standard oder die proprietäre API eines gängigen SPA-Frameworks.
Rich Components lassen sich entweder ĂĽber das script
-Tag von einer Remote-Quelle oder ĂĽber einen Paketmanager wie npm
in das eigene Projekt einbinden und nutzen. Ob es sich dabei um Entwicklungs- oder Laufzeitintegration handelt, entscheidet die Funktionsweise der Komponenten.
Kommt ein Wrapper um die Remote-API eines Fremdsystems herum zum Einsatz, wird die Komponente zwar zur Entwicklungszeit als Abhängigkeit eingebunden, erzeugt aber dennoch Kopplung zur Laufzeit, da sie während der User-Interaktionszeit weitere synchrone Abhängigkeiten auflöst. Sie ist ein typischer Fall für den Wolf im Schafspelz, insbesondere da sie zu mehr Kommunikationsaufwand und somit eben zu einer stärkeren Kopplung führt als andere Integrationsmechanismen, deren Funktionsweise eindeutig ist.
Seit 2020, dem Release der fünften Major-Version des beliebten JavaScript-Bundlers Webpack, hat mit Module Federation ein neuer Mechanismus zum Teilen von Assets für einen Hype gesorgt. Webpack Module Federation ermöglicht es verteilten Systemen, Web-Assets wie JavaScript und CSS zur Laufzeit über HTTP-Endpunkte miteinander auszutauschen. Einzelne Rich Components lassen sich so zur Laufzeit und zu präzisen Zeitpunkten einbinden.
Vereinfacht ausgedrückt ist Module Federation ein Paketmanager zur Laufzeit mit allen Vor- und Nachteilen synchroner Laufzeitintegration. Vor allem die Notwendigkeit zur expliziten Auflösung transitiver Abhängigkeiten zur Laufzeit sorgt für eine außerordentlich starke Kopplung. Probleme, die diese Technologie bereinigen soll, sind beispielsweise ein großer Footprint durch die Nutzung großer, aber möglicherweise ungenutzter Module im eigenen Bundle oder verschiedene Major-Abhängigkeiten von Frameworks.
Oft lassen sich Probleme dieser Art aber auf andere technische oder organisatorische Entscheidungen zurĂĽckfĂĽhren: zum Beispiel einen Team- und Systemschnitt, der sofortigen Konsistenzanforderungen nicht entspricht, oder die forcierte Nutzung spezifischer Frameworks und Bibliotheken.