Kolumne: EnterpriseTales

Web Components in der Welt der Micro Frontends

Hendrik Müller

© S&S Media

Früher bestand eine Webanwendung mehr oder weniger aus nur einer Sammlung von dynamisch erzeugten HTML-Dokumenten, die ggf. mit Ajax Calls und jQuery angereichert wurden. Nahezu die gesamte Komplexität war serverseitig. Erhöhte Anforderungen an Interaktivität haben mittlerweile aber dazu geführt, dass mehr Logik im Client zu finden ist. Single Page Applications (SPAs) sind das Resultat. Durch diese wachsende Logik steigt auch die Komplexität der Anwendungen. Es entstehen Frontend-Monolithen. Eine Wunderwaffe dagegen können Micro Frontends sein. Aber wie gewährleisten wir dann Konsistenz in der gesamten Anwendung und isolieren gleichzeitig die einzelnen Micro Frontends voneinander?

Vom Monolithen zu Microservices

Monolithen haben bei Backend-Entwicklern nur einen bedingt guten Ruf. Sie haben in der Vergangenheit oftmals zu Problemen geführt und tun es auch heute noch oft. Je größer und verworrener der Monolith, desto länger dauert es, Änderungen umzusetzen – wobei die Wahrscheinlichkeit, dass danach eine andere Stelle nicht mehr funktioniert, von Feature zu Feature steigt. Wir erschaffen einen Big Ball of Mud oder wie es das Management sagen würde: Die Produktivität der Teams skaliert nicht mehr. Doch noch mehr Entwickler auf das Problem anzusetzen, macht es im Zweifel noch schlimmer als vorher. Hier wird häufig die schlecht passende Metapher von den neun Frauen verwendet, die ein Kind auch nicht in nur einem Monat bekommen.

Das eigentliche Problem der Wartbarkeit liegt jedoch nicht an der monolithischen Architektur an sich, sondern vielmehr an der häufig fehlenden Modularität. Um diese zu erreichen und damit die Entwicklungsgeschwindigkeit der Teams auf einem konstant hohen Niveau zu halten, müssen wir große Software in möglichst unabhängige und kleinere Stücke zerschneiden. Während die Komplexität des technologischen Aspekts der Software auch bei deren längerer Existenz meist konstant bleibt (die Technologie veraltet höchstens), ist es meist die Fachlichkeit, die mit jedem neuen Feature umfangreicher und komplexer wird. Das führt dazu, dass die Software bei schlechtem Schnitt nicht mehr wartbar ist. Um die Komplexität eines Monolithen aufzubrechen, ist es daher sinnvoll, fachlich (und eben nicht technisch) zu schneiden.

Aus dieser Überlegung heraus hat sich in den letzten Jahren ein alternatives Architekturmuster zum Monolithen im Backend etabliert, die Microservices. Dieser Ansatz zwingt technisch dazu, die Software zu modularisieren. Der richtige Schnitt ist auch hier leider eine Kunst für sich, denn jeder Microservice hat einen abgegrenzten fachlichen Aufgabenbereich.

Die Architektur, die sich ergibt, ist im Idealfall nicht so verworren und dadurch besser wartbar. Service-Abhängigkeiten werden explizit sichtbar und lassen sich besser kontrollieren. Ein weiterer Vorteil von Microservices ist, dass jeder einzelne Service seinen eigenen Technologiestack haben kann und ihn auch unabhängig voneinander aktualisieren darf – Stichwort: Polyglot Microservices. Wir müssen unsere Software also modular bauen, damit die Produktivität der Entwicklerteams, also die schnelle und flexible Weiterentwicklung der Software, auch zukünftig sichergestellt ist.

Der Frontend-Monolith

Nachdem sich im Backend das Verständnis durchgesetzt hat, dass Software modular entwickelt werden sollte, stehen wir in der Frontend-Entwicklung vor ähnlichen Problemen. Was einst mit HTML-Formularen, etwas Ajax und jQuery begann, ist mittlerweile wesentlich komplexer geworden. Früher waren Frontend und Backend noch deutlich näher beieinander. Der Großteil der Logik lag auf dem Server und dieser hat sein Frontend auch selbst ausgeliefert.

Mittlerweile haben höhere Anforderungen an Interaktivität im Client zur Entwicklung von SPAs geführt, die nur noch via HTTP mit dem Backend kommunizieren. Das klingt nicht nur komplexer, sondern ist es auch. So hat es die Problematik der großen und verworrenen Software auch ins Frontend geschafft – wir haben Frontend-Monolithen. Und auch das Resultat ist das gleiche: Wieder skalieren die Teams nicht. Änderungen werden teurer und langsamer. Doch es ist eine Lösung in Sicht: Entsprechend der Microservices-Bewegung im Backend gibt es mittlerweile den Trend hin zum Micro Frontend.

Micro Frontends

Micro Frontends ist die Adaption von Separation of Concerns auf die Frontend-Architektur, vergleichbar mit der Microservices-Idee aus dem Backend. Auch im Frontend wollen wir die große Applikation in kleine Stücke schneiden. Das Frontend wird zur Komposition vieler Micro Frontends. Die verschiedenen technischen Umsetzungsmöglichkeiten einer Micro-Frontend-Architektur würden den Umfang dieses Artikels sprengen. Doch wir können sie grob in zwei Kategorien einteilen: den Standalone-Ansatz, ein Micro Frontend steht für sich allein auf einer Seite (Abb. 1), und den Embeddable-Ansatz, bei dem eine Seite aus mehreren Micro Frontends aufgebaut ist (Abb. 2).

Abb. 1: Standalone Micro Frontends

Abb. 2: Embeddable Frontends

Abb. 2: Embeddable Frontends

Egal wie man seine Micro-Frontend-Architektur umsetzt, es gibt ein Thema, dem besondere Achtung geschenkt werden sollte: die Kommunikation zwischen Micro Frontends. Alle Micro Frontends sollen nicht nur unabhängig voneinander deploybar, sondern auch unabhängig releasebar sein. Sobald ein Micro Frontend mit einem anderen kommuniziert, hat es ein Public API. Und das muss so lange stabil oder zumindest abwärtskompatibel bleiben, bis sichergestellt ist, dass es keiner mehr verwendet – im Zweifel also für immer. Der Frage, was das API eines Micro Frontends ist, werde ich später nachgehen.

Das Problem der Kommunikation zwischen Micro Frontends tritt nur dann auf, wenn sie gemeinsame Daten verwenden. Doch auch in diesem Fall gibt es Möglichkeiten, direkte Kommunikation zu vermeiden. Die Synchronisation könnte etwa über den Server erfolgen (z. B. durch Polling oder Messaging). Um ein kleines API kommt man aber im Zweifel nicht herum, und zwar bei der Initialisierung des Micro Frontends. Im Fall eines Standalone Micro Frontends sind dies hauptsächlich die Informationen aus dem URL. Bei der Embeddable-Variante findet sich dieses API in der Regel im JavaScript wieder.

Einheitliches UX und Design

Ein Ziel der Unterteilung in Micro Frontends ist deren unabhängige Entwicklung. Hier können unterschiedliche Technologien eingesetzt werden, die dennoch idealerweise wie aus einem Guss wirken sollen. Das bezieht sich sowohl auf das Design als auch auf die User Experience (UX).

Die UX über alle Micro Frontends einheitlich zu gestalten, ist primär ein organisatorisches Problem. Um innerhalb eines fachlichen Blocks (und damit eines Teams) eine einheitliche UX zu gewährleisten, sollte sich beispielsweise immer nur eine Version eines Micro Frontends in Produktion befinden. Zudem bedarf es teamübergreifender Absprachen, um dem Anwender auch im Gesamten ein schlüssiges Bedienkonzept zu bieten. Diese Themen lassen sich nicht rein technologisch lösen. Beim Design geht das schon eher. Design bedeutet in der Webentwicklung HTML und CSS. In diesem Zusammenhang ist nur wichtig, dass sich Micro Frontends auf einer Seite nicht gegenseitig im Styling beeinflussen.

Exkurs: Welche Komponententypen gibt es?

Brad Frost hat in seinem Atomic Design eine hierarchische Einteilung von Designelementen vorgeschlagen. Unabhängig von der Frage, ob man Atomic Design umsetzen sollte, sind die verwendeten Metaphern sehr eingängig. Nach Atomic Design gibt es aufeinander aufbauend Atome, Moleküle, Organismen, Templates und Seiten. Atome bilden die kleinste, nicht weiter zerlegbare Einheit, z. B. Buttons oder Icons. Aus mehreren Atomen werden wiederum Moleküle gebaut, etwa ein Textfeld mit Label. Atome und Moleküle sind rein technische Komponenten, sie haben also keinen fachlichen Bezug. Sie bilden die Grundlage für Organismen, beispielsweise eine Artikelsuchleiste in einem Onlineshop. Entsprechend kombinieren Organismen die technischen Komponenten und bringen sie in einen fachlichen Kontext. Darauffolgend gibt es im Atomic Design noch Templates und Seiten, auf die wir hier aber nicht näher eingehen.

Wenn wir von Micro Frontends sprechen, befinden wir uns nach Atomic Design also immer mindestens auf der Ebene von Organismen.

Die gemeinsame Komponentenbibliothek

Gibt es gemeinsame Komponenten, die wir in verschiedenen Micro Frontends einsetzen wollen, haben wir redundantes HTML und CSS in mehreren Micro Frontends. In der Regel handelt es sich bei solchen Komponenten um Atome und Moleküle nach Atomic Design. Wie können wir diese Komponenten jetzt Micro-Frontend-übergreifend verwenden? Generell gibt es zwei mögliche Herangehensweisen: Die erste Möglichkeit ist, dass sich jedes Team das entsprechende HTML und CSS in sein eigenes Projekt kopiert. Bei der zweiten Variante werden die gemeinsamen Komponenten zentral zusammen mit ein bisschen JavaScript ausgeliefert. Die zweite Variante beinhaltet eine gravierende Herausforderung: Sie bedeutet die Einführung einer zentralen Komponentenbibliothek für mehrere Micro Frontends. Folglich verstoßen wir hier gegen das Share-Nothing-Prinzip, das auch aus der Microservices-Welt bekannt ist. Ein solcher Verstoß muss eine bewusste Entscheidung sein – mit all seinen Konsequenzen. Entscheidet man sich dafür, stellen sich viele organisatorische Fragen. Wer verantwortet die gemeinsame Bibliothek? Wie ist der Prozess für neue Features? Wie verhindere ich, dass das Team, das die Weiterentwicklung der Bibliothek verantwortet, zum Flaschenhals wird?

Eine wichtige Bedingung dabei ist, dass das API der gemeinsamen Komponenten wirklich stabil ist. Nur so können alle Micro Frontends auf der neuesten Version der Bibliothek funktionieren.

Betrachtet man die genannten Herausforderungen, ist es wahrscheinlich der einfachste Weg, mögliche gemeinsame Komponenten zuerst per Copy and Paste wiederzuverwenden. Sobald sie etabliert sind und sich ein stabiles API herauskristallisiert hat, können die Komponenten in die zentrale Bibliothek übertragen werden.

Neben dem organisatorischen Aufwand gibt es auch ein technisches Problem. Jedes Micro-Frontend-Team ist frei in der Wahl seiner Technologie. Wie können wir dann Komponenten ausliefern, die trotzdem von allen verwendet werden können? Eine mögliche Lösung dafür sind Web Components.

Framework-übergreifend: Web Components

Web Components sind Teil des DOM APIs des Browsers und damit ein Webstandard. Sie ermöglichen es, eigene, wiederverwendbare und selbstständige HTML-Elemente zu definieren. Wenn man von Web Components spricht, meint man in der Regel drei Bestandteile: Custom Elements, Shadow DOM und HTML Templates.

Custom Elements ermöglicht es, eigene HTML-Elemente zu definieren. Hierzu kann zu einem Tagnamen eine eigene Implementierung registriert werden. Dazu gehören auch Callbacks, die der Browser dann bei Attributänderungen oder beim Verschieben im DOM aufruft.

Mit Shadow DOM ist es möglich, das DOM innerhalb eines Custom Elements nach außen abzuschirmen. Im Wesentlichen wird es benötigt, um das Styling innerhalb eines Custom Elements vom Rest der Seite zu isolieren und so ungewollte Wechselwirkungen zu verhindern. Zusätzlich kann mit Shadow DOM auch gesteuert werden, welche DOM Events ein Custom Element verlassen.

Zu HTML Templates gehören das <template>– und auch das <slot>-Element. Das <template>-Element ermöglicht es, effizient und mehrfach wiederverwendbare HTML-Fragmente zu schreiben. Das <slot>-Element ist ein Platzhalter innerhalb einer Web Component, um von außen hineingereichtes HTML einzubetten. Das ist unter anderem notwendig, wenn man eigene Containerelemente umsetzen will.

Die Web Components Standards ermöglichen es uns also, gekapselte Komponenten zu schreiben, die weitgehend isoliert von dem Rest der Seite sind. Bei genauerer Betrachtung geht es dabei aber primär um das DOM und die optische Darstellung. Als Technologie, um wiederverwendbare Designkomponenten umzusetzen, sind Web Components daher durchaus geeignet.

Ein weiteres Einsatzszenario für Web Components ist es, sie als Containertechnologie für Micro Frontends zu verwenden. Auch wenn Web Components kein Sicherheitsfeature mit perfekter Isolation sind, kann man sie nutzen, um Micro Frontends auf einer Seite zumindest zu einem gewissen Grad voneinander zu trennen. Das erreicht man, indem jedes Micro Frontend in eine Web Component verpackt wird.

Ein wichtiger Punkt ist beim Einsatz von Web Components noch zu beachten: Es ist technisch momentan nicht möglich, Web Components innerhalb einer Seite zu scopen. Es kann also nicht mehrere Implementierungen für einen Tagnamen auf einer Seite geben. Das heißt, dass man sicherstellen muss, dass ein exklusives Präfix oder Ähnliches im Tagnamen verwendet wird, um Eindeutigkeit zu gewährleisten.

Das API einer Web Component

Teilt man Web Components mit mehreren Teams – etwa als Integrationstechnologie oder für gemeinsame UI-Komponenten – bedeutet das, dass die Komponente ein öffentliches API haben muss, das von anderen Teams verwendet werden kann. Dazu muss man sich erst einmal bewusst machen, was eigentlich alles zum API einer Web Component gehört, da der Umfang deutlich größer ist, als man vielleicht annimmt: Attribute, Events, Slots, Properties und Methoden sowie auch das Verhalten im Styling. Zudem gilt zu beachten, dass Web Components einen Zustand haben, der sich über die Zeit ändern kann, beispielsweise ein Attributwert. Führt das dazu, dass sich die Darstellung der Web Component ändert? Oder hat nur der initiale Wert Auswirkungen? Es kann auch sein, dass einem Attribut ein ungültiger Wert zugewiesen wird, wie geht man damit um? Sieht man sich diese Fragen an, merkt man schnell, dass es nicht einfach ist, eine Web Component mit stabilem oder abwärtskompatiblem API zu bauen.

Fazit

In der Welt der Micro Frontends können Web Components wichtige Positionen einnehmen. Generell eignen sie sich durch ihre Framework-agnostische Natur als technische Grundlage für gemeinsame UI-Elemente oder als Containertechnologie, um Micro Frontends miteinander zu integrieren. Doch Technologie und so auch Web Components sind kein Allheilmittel.

Insbesondere die Umsetzung einer gemeinsamen UI-Bibliothek, die von mehreren Micro Frontends genutzt werden soll, ist eine organisatorische Herausforderung. Man verstößt hier absichtlich gegen das Share-Nothing-Prinzip und muss sich dessen auch bewusst sein. Eine globale Abhängigkeit muss gut gehütet werden, damit sie wartbar bleibt. Das erfordert Disziplin. Das API muss eigentlich für immer stabil bzw. abwärtskompatibel bleiben. Bevor also eine neue Komponente in die globale Bibliothek wandern kann, sollte sichergestellt werden, dass sie dort richtig ist. Man kann eine Komponente besser in mehrere Micro Frontends kopieren, als sie vorschnell in den gemeinsamen globalen Fundus aufzunehmen.

Will man mehrere Micro Frontends auf einer Seite einsetzen, kann man sie mit Hilfe von Web Components, die hier quasi die Isolationsschicht bilden, komponieren. Diese Isolation erstreckt sich aber nicht auf globale JavaScript-Objekte. Deren Zugriff aus Web Components heraus wird nicht verhindert. Damit bieten sie in diesem Punkt keine höhere Sicherheit. Dynamisches Nachladen und das Scopen von Styling und DOM Events sind jedoch realisierbar. Was zudem im Zusammenhang mit Web Components zu einem Problem führen kann, ist die Tatsache, dass es momentan technisch nicht möglich ist, Custom Elements innerhalb einer Seite zu scopen, das heißt, ihr Tagname wird global registriert und muss daher eindeutig sein.

Potenziell stößt man auf weniger Probleme, wenn nicht mehrere Micro Frontends auf einer Seite verwendet werden. Generell gilt, dass Micro Frontends so wenig wie möglich – am besten gar nicht – miteinander kommunizieren sollten. Denn ihr API muss stabil oder abwärtskompatibel sein, damit es unabhängig releasebar und damit langfristig wartbar bleibt.

Zusammengefasst können Web Components bei der Realisierung von Micro Frontends technologisch eine zentrale Rolle einnehmen. Bei der Einführung von zentralen Komponenten muss man jedoch aufpassen, dass man dadurch keine organisatorischen Probleme verursacht.

Geschrieben von
Hendrik Müller

Hendrik Müller ist Enterprise Developer bei der OPEN KNOWLEDGE GmbH in Oldenburg. Mit dem Schwerpunkt auf Webtechnologien begleiten ihn momentan Angular und Web Components durch den Tag. Abseits davon beschäftigt er sich leidenschaftlich mit dem Design von Programmiersprachen.

Kommentare

Hinterlasse einen Kommentar

avatar
4000
  Subscribe  
Benachrichtige mich zu: