In all den Jahren haben Sie als Frontend-Entwickler Monolithen geschrieben, obwohl Sie verstanden haben, dass dies eine schlechte Angewohnheit war. Sie haben Ihren Code in Komponenten aufgeteilt,
require
oder
import
und npm-Pakete in package.json definiert oder Git-Repositorys in Ihrem Projekt generiert, aber Sie haben trotzdem einen Monolithen geschrieben.
Es ist Zeit, die Position zu ändern.
Warum kann Ihr Code als Monolith betrachtet werden?
Von Natur aus sind alle Frontend-Anwendungen monolithisch - mit Ausnahme von Anwendungen, die Micro-Frontends implementieren. Der Grund dafür ist, dass Sie mithilfe der React-Bibliothek entwickeln und zwei Teams die Arbeit erledigen. Beide sollten dieselbe Version von React verwenden und sich gegenseitig über Aktualisierungen auf dem Laufenden halten. Dies bedeutet, dass sie Konflikte mit der Zusammenführung des Codes ausnahmslos lösen. Sie sind in der Codebasis nicht völlig unabhängig voneinander. Sie verwenden wahrscheinlich alle dasselbe Repository und ein Build-System. Microservices können vor monolithischen Anwendungen sparen! Aber wie so? Immerhin sind sie für das Backend! * unglaubliche Überraschung *
Was sind Microservices?
In einfachen Worten, Microservices sind eine Entwicklungstechnik, die es Entwicklern ermöglicht, unabhängige Funktionen (Releases) für verschiedene Teile der Plattform bereitzustellen, und gleichzeitig brechen sich Releases nicht gegenseitig. Unabhängige Lieferungen ermöglichen es ihnen, isolierte oder lose gekoppelte Dienste zu sammeln. Es gibt mehrere Regeln, die diese Architektur robuster machen. Kurz gesagt, sie können wie folgt definiert werden: Jeder Dienst sollte klein sein und nur eine Aufgabe ausführen. Daher sollte das Team, das daran arbeitet, auch klein sein. Wie groß ein Projekt und ein Team sein können,
erklären James Lewis und Martin Fowler:
Entwickler, die mit Microservices interagieren, nennen unterschiedliche Größen. Die größten von ihnen erfüllen die Zwei-Pizza-Team- Strategie von Amazon - nicht mehr als 10-12 Personen. Die umgekehrte Pole - Teams von 5-6 Personen, von denen jede einen Dienst unterstützt.
Hier ist ein Diagramm, das den Unterschied zwischen einem Monolithen und Mikrodiensten erklärt:

Aus dem Diagramm ist ersichtlich, dass jeder Dienst im Microservice-System eine separate Anwendung ist, mit Ausnahme der Benutzeroberfläche - es bleibt ein einziges Ganzes! Wenn alle Services von einem Team unterstützt werden, besteht das Risiko, dass das Frontend-Team mit dem Wachstum des Unternehmens nicht mehr mit der Benutzeroberfläche Schritt hält. Dies ist die Schwachstelle dieser Architektur.

Architektur kann organisatorische Probleme mit sich bringen. Angenommen, das Unternehmen ist gewachsen und hat eine flexible Entwicklungsmethode eingeführt (ich spreche von Agile). Sie erfordern kleine funktionsübergreifende Teams. In unserem abstrakten Beispiel werden Manager natürlich beginnen, die Aufgaben des Frontends und des Backends zu trennen, und die funktionsübergreifenden Teams werden nicht wirklich funktionsübergreifend sein. Und alle Bemühungen werden vergebens sein: Das Team mag flexibel aussehen, aber tatsächlich wird es stark gespalten sein. Das Management eines solchen Teams ist nichts für schwache Nerven. Bei jedem Meeting wird eine Frage gestellt: Gibt es genug Frontend-Aufgaben, gibt es genug Backend-Aufgaben im Sprint? Um diese und viele andere Probleme zu lösen, tauchte vor einigen Jahren die Idee der Mikrofront auf, die schnell an Popularität gewann.
Lösung des Problems: Mikrofronts
Die Lösung sieht ziemlich offensichtlich aus, da ähnliche Prinzipien bei der Arbeit an Backend-Diensten seit langem erfolgreich angewendet werden: Aufteilen eines monolithischen Frontends in kleine UI-Fragmente. Die Benutzeroberfläche ist den Diensten jedoch nicht ganz ähnlich - sie ist eine Schnittstelle zwischen dem Endbenutzer und dem Produkt. Sie muss durchdacht und systematisch sein. Darüber hinaus werden im Zeitalter von Anwendungen mit nur einer Seite ganze Anwendungen über einen Browser auf der Clientseite gestartet. Dies sind keine einfachen HTML-Dateien mehr, sondern komplexe Komponenten, die verschiedene Benutzeroberflächen und Geschäftslogik enthalten können. Vielleicht ist es jetzt notwendig, Mikrofronts zu definieren.
Das Prinzip der Mikrofronts: Die Präsentation einer Website oder Webanwendung als eine Reihe von Funktionen, für die unabhängige Teams verantwortlich sind. Jedes Team hat seine eigene Mission, sein eigenes Arbeitsfeld, auf das es sich spezialisiert hat. Das Team ist funktionsübergreifend und entwickelt sich weiter
den gesamten Zyklus - von der Datenbank bis zur Benutzeroberfläche ( micro-fontend.org ).
Meine aktuelle Erfahrung zeigt, dass es für viele Unternehmen schwierig sein kann, die oben vorgeschlagene Architektur zu akzeptieren. Für andere erlaubt die Belastung durch den alten Code nicht den Übergang zu einer neuen Architektur. Daher ist eine reibungslosere, einfachere und zuverlässigere Art der Migration erforderlich. Nachdem ich die Architektur genauer untersucht habe, werde ich versuchen, meine Vision zur Lösung des Problems anzubieten. Machen Sie sich mit der Terminologie vertraut, bevor Sie sich mit den Details befassen.
Allgemeine Struktur und etwas mehr TerminologieStellen Sie sich vor, wir teilen die Struktur einer monolithischen Anwendung vertikal durch Geschäftsfunktionen. Wir werden mehrere kleinere Anwendungen mit der gleichen Struktur wie die monolithische Anwendung erhalten. Wenn wir jedoch zusätzlich zu diesen kleinen monolithischen Anwendungen eine spezielle Anwendung hinzufügen, werden die Benutzer damit interagieren. Es wird wiederum die Benutzeroberfläche dieser kleinen Anwendungen integrieren. Wir nennen diese Ebene den Link, da die UI-Elemente jedes Microservices in einer einzigen Schnittstelle zusammengefasst werden - dies ist die direkteste Implementierung des Mikrofronts. * aufrichtige Bewunderung *

Um es klarer zu machen, werde ich im Folgenden jede kleine monolithische Anwendung als
Mikroanwendung bezeichnen , da es sich nicht nur um Mikrodienste handelt, sondern um eigenständige Anwendungen - jede von ihnen verfügt über UI-Elemente und jede von ihnen repräsentiert eine vollwertige Geschäftsfunktion. Wie Sie wissen, ist das heutige Front-End-Ökosystem sehr vielfältig und kann sehr komplex sein. Und solche einfachen, offensichtlichen Lösungen sind im Produktimplementierungsprozess möglicherweise nicht angemessen.
Zu lösende Probleme
Als die Idee zu diesem Artikel geboren wurde, habe ich ein Thema auf Reddit gestartet, um darüber zu diskutieren. Dank der Community-Mitglieder und ihres Feedbacks kann ich die Probleme auflisten, die behoben werden müssen.
Problem Nummer 1: Erzielen Sie ein konsistentes und konsistentes Verhalten über die Benutzeroberfläche, wenn wir mehrere vollständig autonome Mikroanwendungen haben
Es gibt kein Allheilmittel, aber es gibt die Idee, eine gemeinsame UI-Bibliothek zu erstellen, die auch eine unabhängige Mikroanwendung wäre. In diesem Fall müssen alle anderen Mikroanwendungen von dieser UI-Bibliothek abhängen. Und das tötet ihre Unabhängigkeit.
Eine weitere Option besteht darin, allgemeine
CSS-Variablen auf Stammebene zu erstellen. Der Vorteil dieser Lösung ist, dass wir ein globales benutzerdefiniertes Thema für alle Anwendungen erhalten.
Oder wir können SASS-Variablen und Verunreinigungen allen Teams gemeinsam machen. Zu den Nachteilen dieses Ansatzes gehören die wiederholte Implementierung von UI-Elementen und die Notwendigkeit einer ständigen Überprüfung des Designs ähnlicher Elemente in allen Mikroanwendungen.
Problem Nummer 2: Stellen Sie sicher, dass ein Team das CSS eines anderen Teams nicht neu schreibt
Erstens können Sie den Umfang von CSS mithilfe von Selektoren einschränken, die aus dem Namen der Mikroanwendung bestehen. Indem Sie diese Einschränkung auf die mittlere Ebene setzen, können Sie die Gesamtentwicklungszeit reduzieren, gleichzeitig erhöht sich jedoch die Verantwortung für die mittlere Ebene.
Zweitens können Sie jede
Mikroanwendung zu einer
benutzerdefinierten Webkomponente machen . Der Vorteil dieses Ansatzes besteht darin, dass sich der Browser mit der Einschränkung befasst. Alles hat jedoch seinen Preis: Mit dem Shadow DOM ist es fast unmöglich, auf der Serverseite zu rendern. Darüber hinaus werden benutzerdefinierte Elemente
von Browsern nicht zu 100%
unterstützt - insbesondere, wenn Sie IE-Unterstützung benötigen.
Problem Nummer 3: Machen Sie globale Informationen für verschiedene Mikroanwendungen gleich
Dieses Problem ist eines der häufigsten, aber es ist recht einfach zu lösen. HTML5 verfügt über leistungsstarke Funktionen, die von den meisten Front-End-Entwicklern kaum erforscht werden.
Eine dieser Funktionen sind benutzerdefinierte Ereignisse, mit denen Sie Informationen für Mikroanwendungen festlegen können.
Eine Pub-Sub- oder T39-Implementierung kann ebenfalls hilfreich sein. Wenn Sie einen subtileren globalen Status-Handler benötigen, können Sie einen kleinen allgemeinen Redux implementieren - dies ergibt eine reaktionsschnellere Architektur.
Problem Nummer 4: Wenn alle Mikroanwendungen autonom sind, wie wird das Routing auf der Clientseite durchgeführt?
Die Lösung des Problems hängt von der Implementierung ab. Alle wichtigen modernen Frameworks verfügen über leistungsstarke clientseitige Routing-Mechanismen, die den Status des Browserverlaufs verwenden. Das Problem ist, welche Anwendung für die aktuelle Route verantwortlich ist und wann genau.
Mein pragmatischer Ansatz besteht darin, einen gemeinsamen Client-Router zu erstellen, der nur für Routen der obersten Ebene verantwortlich ist, und der Rest ist den entsprechenden Mikroanwendungen ausgeliefert. Angenommen, wir haben eine Routendefinition
/content/:id.
Der gemeinsame Router löst den c
/content
und die aufgelöste Route wird an ContentMicroApp übergeben. ContentMicroApp ist ein eigenständiger Server, der nur mit
/:id
aufgerufen wird.
Problem Nummer 5: Brauchen wir wirklich SSR (serverseitiges Rendering)? Ist dies bei Verwendung von Mikrofronts möglich?
Das serverseitige Rendern ist nicht einfach. Wenn Sie Mikroanwendungen mithilfe von Iframes bündeln möchten, vergessen Sie das serverseitige Rendern. Ebenso sind Webkomponenten zum Binden nicht stärker als Iframes. Wenn jedoch jede der Mikroanwendungen in der Lage ist, serverseitigen Inhalt zu rendern, ist die Verbindungsschicht nur für das Kombinieren von HTML-Fragmenten auf der Serverseite verantwortlich.
Problem 6: „Die Integration in die vorhandene Umgebung ist wie Luft erforderlich! Wie macht man das? "
Für die Integration in bestehende Systeme möchte ich meine Vision beschreiben, die ich als „
schrittweise Implementierung “ bezeichne.
Zunächst müssen wir die Middleware-Schicht so implementieren, dass sie die Funktionalität eines transparenten Proxyservers aufweist. Danach können wir das vorhandene System als
Mikroanwendung (
LegacyMicroApp ) definieren, indem wir eine spezielle Route dazu deklarieren. Der gesamte Datenverkehr, der die Verbindungsebene erreicht, wird transparent an das vorhandene System weitergeleitet, da wir noch keine anderen Mikroanwendungen haben.
Der nächste Schritt ist die schrittweise Implementierung. Wir werden ein kleines Stück
LegacyMicroApp nehmen , die
Hauptnavigation entfernen und durch eine Abhängigkeit ersetzen. Diese Abhängigkeit ist eine Mikroanwendung, die mit der brandneuen brillanten Technologie NavigationMicroApp implementiert wurde.
Jetzt fängt
LegacyMicroApp alle Routen durch die NavigationMicroApp-Abhängigkeit ab und verarbeitet sie intern.
In ähnlicher Weise werden wir dann die Fußzeile neu erstellen.
Also werden wir weiterhin ein Stück
LegacyMicroApp abbeißen, bis nichts mehr davon übrig ist.
Problem Nummer 7: Orchestrieren Sie die Clientseite, damit Sie die Seite nicht jedes Mal neu laden müssen
Die Middleware-Schicht löst Probleme auf der Clientseite, jedoch nicht auf der Serverseite. Auf der Clientseite können wir durch Herunterladen eines einzelnen HTML-Codes beim Ändern von URLs keine einzelnen Teile laden. Daher benötigen wir einen Mechanismus, der Fragmente asynchron lädt. Das Problem ist, dass diese Fragmente Abhängigkeiten aufweisen können und diese Abhängigkeiten auf der Clientseite aufgelöst werden müssen. Dies bedeutet, dass die Mikrofrontend-Lösung einen Mechanismus zum Laden von Mikroanwendungen und zum Implementieren der Abhängigkeitsinjektion bieten sollte.
Die oben aufgeführten Probleme können zu folgenden Themen zusammengefasst werden:
Client-Seite- Orchestrierung
- Routing
- Mikroisolation
- Anwendungsinteraktion
- Unity UI-Mikroanwendungen
Serverseite- Server-Rendering
- Routing
- Abhängigkeitsmanagement
Flexible und leistungsstarke, aber einfache Architektur
Aus diesem Grund hat es sich gelohnt, den Anfang des Artikels zu ertragen! Die Hauptelemente und Anforderungen der Mikrofrontend-Architektur haben sich endlich herausgebildet;)
Anhand der Anforderungen und Bedenken begann ich, eine Lösung namens
microfe zu entwickeln . * Vorfreude auf Feedback *
Hier werde ich die Architektur des Projekts skizzieren und kurz seine Hauptkomponenten beschreiben.
Der einfachste Weg ist der Start auf der Clientseite, die drei separate Hauptstrukturen aufweist:
AppsManager, Loader, Router sowie einen weiteren,
MicroAppStore .
AppsManagerAppsManager ist der Kern der clientseitigen Orchestrierung von Mikroanwendungen. Das Hauptziel von AppsManager ist das Erstellen eines Abhängigkeitsbaums. Sobald alle Abhängigkeiten aufgelöst sind, startet AppsManager die Mikroanwendung.
LaderEin weiterer wichtiger Bestandteil der clientseitigen Orchestrierung ist Loader. Er ist verantwortlich für das Herunterladen von Anwendungen für die Client-Seite.
RouterUm clientseitiges Routing durchzuführen, habe ich Router in Microfe implementiert. Im Gegensatz zu herkömmlichen clientseitigen Routern verfügt der Mikrofe-Router über eine eingeschränkte Funktionalität. Es werden keine Seiten verarbeitet, sondern Mikroanwendungen. Angenommen, wir haben URL
/content/detail/13
und ContentMicroApp. In diesem Fall verarbeitet der Mikrofe-Router die URL zu
/content/*
und ruft den Teil ContentMicroApp
/detail/13
.
MicroappporeUm die Client-Interaktion zwischen Mikroanwendungen zu lösen, habe ich MicroAppStore in microfe implementiert. Es hat die gleiche Funktionalität wie die Redux-Bibliothek, jedoch mit einer Einschränkung: Es ist flexibler in Bezug auf asynchrone Datenmodifikation und Reducer'a-Deklaration.
***.
Die Server-Seite ist vielleicht etwas komplizierter zu implementieren, hat aber eine einfachere Struktur. Es besteht aus zwei Hauptteilen - StitchingServer und MicroAppServer.
Mikroappserver

Die kleinstmögliche MicroAppServer-Funktionalität kann ausgedrückt werden als: init und serve.
Wenn MicroAppServer gestartet wird, sollte zunächst SticthingServer aufgerufen und ein Endpunkt bei der angekündigten Mikroanwendung registriert werden. Es definiert die Abhängigkeiten, Typen und URLs des MicroAppServer-Schemas. Ich denke, dass es unnötig ist, über Serve zu sprechen - hier gibt es nichts Interessantes.
Stitching Server

Mit StitchingServer können Sie einen Endpunkt bei MicroAppServers registrieren. Wenn sich MicroAppServer bei StichingServer registriert, zeichnet StichingServer die MicroAppServer-Deklaration auf.
Später verwendet StitchingServer die Anzeige, um MicroAppServices von der angeforderten URL aufzulösen.
Nachdem MicroAppServer und alle seine Abhängigkeiten zugelassen wurden, wird die entsprechende öffentliche URL in den Namen aller entsprechenden Pfade in CSS, JS und HTML angezeigt. Ein weiterer Schritt besteht darin, CSS-Selektoren ein eindeutiges MicroAppServer-Präfix hinzuzufügen, um Konflikte zwischen Mikroanwendungen auf der Clientseite zu vermeiden.
Dann tritt die Hauptaufgabe von StitchingServer in die Szene ein: Zusammenstellen aller empfangenen Teile und Zurückgeben der gesamten HTML-Seite.
Ein paar Worte zu anderen Implementierungen
Noch bevor der Begriff Mikrofrontend im Jahr 2016 auftauchte, versuchten viele große Unternehmen, ähnliche Probleme zu lösen - zum Beispiel Facebook mit seiner
BigPipe .
Jetzt gewinnt die Idee an Fahrt. Unternehmen jeder Größe interessieren sich für dieses Thema und investieren Zeit und Geld in es. Beispielsweise stellte Zalando den Open Source-Code für seine
Project Mosaic- Lösung bereit. Ich kann sagen, dass Mikrofe und Projektmosaik ähnliche Ansätze verfolgen, jedoch mit einigen wesentlichen Unterschieden. Wenn Microfe auf vollständig dezentrales Routing zurückgreift, um jede Mikroanwendung unabhängiger zu machen, bevorzugt Project Mosaic das zentralisierte Routing und die Musterdefinition für jede Route. Übrigens macht es Project Mosaic einfach, AB-Tests und dynamische Vorlagengenerierung im laufenden Betrieb durchzuführen.
Es gibt insbesondere andere Ansätze, bei denen iframs als Verbindungsschicht verwendet wird - offensichtlich nicht auf der Serverseite, sondern auf der Clientseite. Dies ist eine sehr einfache Lösung, die keine spezielle Serverstruktur und die Einbeziehung von DevOps erfordert. Es kann vom Front-End-Team unabhängig implementiert werden, was bedeutet, dass es weniger organisatorische Probleme für das Unternehmen verursacht und weniger kostet.
Es gibt immer noch ein
Single-Spa- Framework. Das Projekt stützt sich auf Namenskonventionen für jede Anwendung, um Mikroanwendungen zuzulassen und herunterzuladen. Einfach die Idee zu erfassen und Mustern zu folgen. Das Framework kann daher hilfreich sein, um das System in Ihrer lokalen Umgebung kennenzulernen und damit zu experimentieren. Das Minus des Projekts ist, dass Sie jede Mikroanwendung streng definiert erstellen müssen - andernfalls akzeptiert das Framework sie möglicherweise nicht.
Fazit (und Links)
Ich denke, dass das Thema Mikrofronts im Laufe der Zeit genauer betrachtet wird. Wenn es die Aufmerksamkeit von immer mehr Unternehmen auf sich zieht, wird dieses Konzept in großen Teams zur Standardentwicklungsmethode. Es wird für jeden Front-End-Entwickler nützlich sein, sich in naher Zukunft mit dieser Architektur vertraut zu machen und wertvolle Erfahrungen damit zu sammeln.
Micro Fe App RegistrierungsserverMicro-Front-End-Infrastruktur