Der Begriff "Micro-Frontend" hat sich als Erweiterung der Microservice-Idee auf Frontends etabliert. In diesem Artikel vergleichen wir die aus unserer Sicht wichtigsten Ansätze und Patterns wie SSI (Server Side Includes), JavaScript mit Ajax und Web Components und berichten von unseren eigenen praktischen Erfahrungen damit. Wie bei allen grundlegenden Architekturentscheidungen sollte man sich vor dem Einsatz von Mikro-Frontends über deren Vor- und Nachteile im Klaren sein. Ist dies nicht der Fall, lässt sich diese Grundsatzentscheidung später nur mit erheblichem Aufwand wieder rückgängig machen. Neben den architektonischen und technologischen Aspekten gehen wir auch darauf ein, was der Einsatz von Mikro-Frontends für die teamübergreifende Zusammenarbeit bedeutet.
Nach unserer Erfahrung lässt sich der Einsatz von Frontends aus funktionaler Sicht in drei allgemeine Geschäftsszenarien unterteilen:
In den letzten Jahren hat sich in vielen Projekten der Architekturstil der Microservices durchgesetzt. Die übergeordnete Idee dabei ist, das Gesamtsystem in kleinere, einfach zu bedienende Einheiten zu zerlegen mit dem Ziel, einen engen funktionalen Zusammenhalt innerhalb der jeweiligen Microservices und eine möglichst geringe Kopplung untereinander zu erreichen. So können die Komponenten des Gesamtsystems unabhängig voneinander weiterentwickelt werden. Zu beachten ist, dass die funktionale Gliederung selbst auch der kritische Teil ist, denn sie entscheidet darüber, ob das Ziel kleiner, unabhängiger Einheiten erreicht wird oder ob nur ein Spaghetti-Code-Monolith mit verteilten Services implementiert wurde. Richtig umgesetzt versprechen Microservices die in Tabelle 2 skizzierten Vorteile.
Gerade für Services, die primär über APIs miteinander verbunden sind oder diese als öffentliche APIs zur Verfügung stellen, scheinen die Vorteile sehr wünschenswert zu sein. Doch gilt dies auch, wenn eine Schnittstelle für Nutzer bereitgestellt werden muss, deren Anwendungsfall das Zusammenspiel mehrerer Microservices erfordert? Können sie in diesem Fall noch in den Genuss der Vorteile von Microservices kommen? Oder bringt die Erweiterung wesentliche Nachteile mit sich? Um diese Fragen zu beantworten, müsst ihr in einem ersten Schritt feststellen, welchen Architekturansatz ihr für das Frontend gewählt haben. Neben der funktional getriebenen Sichtweise am Anfang, sprechen wir hier von der technischen Sicht auf Micro-Frontends.
Grundsätzlich gibt es zwei Ansätze: Frontend-Monolith und Frontend-Integration.
Der Frontend-Monolith überspannt alle Microservices mit einem zentralen Frontend. Bei der Frontend-Integration hingegen ist jeder Microservice für sein eigenes Frontend verantwortlich - und die Herausforderung besteht darin, diese für den Benutzer sinnvoll miteinander zu integrieren. In den letzten Jahren hat sich der Begriff "Micro-Frontend" auch als "Frontend-Integration" etabliert. ThoughtWorks hat den Begriff erstmals 2016 in ihrem Technology Radar verwendet(mehr dazu).
Beide Ansätze haben ihre Daseinsberechtigung für bestimmte Anwendungsfälle sowie die damit verbundenen Stärken und Schwächen. Wir vergleichen die verschiedenen Ansätze, indem wir prüfen, ob die oben genannten Vorteile von Microservices beibehalten werden können. Darüber hinaus geben wir Einblicke aus unserer Erfahrung, ob sich eventuell entstehende Nachteile im jeweiligen Geschäftsszenario auch negativ auswirken.
Wenn alle Microservices APIs für ihre Geschäftsfunktionen zur Verfügung stellen, liegt es nahe, diese über eine zentrale Frontend-Komponente anzusprechen und damit für die Nutzer einheitlich zugänglich zu machen.
In diesem Szenario wird das Frontend als eigenständige Einheit entwickelt und kommuniziert über definierte APIs mit den verschiedenen Services. Backend-Teams stellen lediglich Funktionen über (versionierte) APIs zur Verfügung, und ein eigenes Team erstellt das Frontend für alle anderen Teams (siehe Abbildung 1). So entsteht eine horizontale Frontend-Schicht, von der alle anderen Teams abhängen. Im Sinne einer Microservice-Architektur sind die Teams nicht mehr unabhängig. Funktionen werden dem Benutzer erst dann zur Verfügung gestellt, wenn ein neues Frontend-Release ausgeliefert wurde.
Eine Gateway-Schicht kann den API-Zugang vereinfachen. Darüber hinaus kann die Gateway-Schicht auch die Authentifizierung oder das Caching übernehmen. Wenn man plant, mehrere Frontend-Monolithen (native App, Web) zu verwenden, kann man diese über mehrere spezialisierte Gateways verbinden (mehr dazu). Dennoch sollte man immer klare Regeln aufstellen, um die Wartbarkeit zu gewährleisten und zu verhindern, dass neben der Datentransformation auch Teile der Geschäftslogik in das Gateway diffundieren.
Durch die einheitliche Umsetzung von UI und UX, sowohl innerhalb der vertikalen Microservices als auch in übergreifenden Use Cases, die sich über mehrere Microservices erstrecken, wirkt die Anwendung für den Endanwender wie ein klassischer Monolith. Tabelle 3 zeigt, welchen Einfluss der Frontend-Monolith auf die Vorteile der Microservices hat.
Wenn ich nur eine kleine Anzahl von Teams oder wenig Funktionalität abzudecken habe, kann ein Frontend-Monolith, also ein Layer, eine gute Lösung sein; habe ich hingegen eine größere Anzahl von Teams oder eine sehr breit angelegte Funktionalität, also Domänen im Sinne eines Domain Driven Design (DDD), sind das erste Anzeichen dafür, dass eine Mikro-Frontend-Architektur in Betracht gezogen werden sollte. Dabei sollte eines der in den folgenden Abschnitten beschriebenen Frontend-Integrationsmuster auf seine Anwendbarkeit hin überprüft werden.
Wenn zum Zeitpunkt der Architekturentscheidung unklar ist, wie die Domäne aufgeteilt wird oder wie viele Teams gebildet werden sollen, ist zunächst eine einfache Lösung zu wählen, die weder zu viele Entscheidungen vorwegnimmt noch zu einem großen Ramp-up-Aufwand führt. Es spricht also nichts dagegen, sich zunächst für eine Frontend-Schicht zu entscheiden - das entbindet aber nicht von der Notwendigkeit, diese Schicht sehr sauber zu gestalten und zu entwickeln. Klare Domänen sollten auch in der Frontend-Schicht zu finden sein, und Domäne und Technologie sollten voneinander getrennt werden.
Wenn es später Gründe gibt, die Architektur zu einem Mikro-Frontend weiterzuentwickeln, ist das mit deutlich weniger Aufwand verbunden. Im Hinblick auf die Geschäftsszenarien gibt es keine klare Präferenz für den Frontend-Monolithen. Für den B2C-Bereich sollte man jedoch die Anforderungen an die Entwicklungsgeschwindigkeit (z.B. durch unabhängige Deployments) und die Erreichbarkeit der notwendigen Skalierbarkeit der Anwendung prüfen. Beurteilt insbesondere, ob einzelne Business-Komponenten im Frontend stärker frequentiert werden könnten und somit separat skalierbar bleiben müssen.
Bei der Frontend-Integration ist das Frontend Teil des jeweiligen Microservices und wird von dessen Team gemeinsam entwickelt (siehe Abbildung 2). Für die Integration mit anderen Microservices gibt es mehrere Möglichkeiten. Im einfachsten Fall sind die funktionalen Kontexte der Microservices so eindeutig, dass die Integration über eine gängige Navigationsstruktur einfach zu realisieren ist. HTTP-Verknüpfungen zwischen den Kontexten erleichtern es dem Benutzer, bei Bedarf zwischen verschiedenen Kontexten zu springen. Diese Variante stößt schnell an ihre Grenzen, sobald Inhalte aus verschiedenen Kontexten dem Nutzer immer parallel präsentiert werden müssen. Eine fortgeschrittene Integration kann auf verschiedene Weise erreicht werden: Integration zur Build-Zeit, Integration auf dem Server oder Integration auf dem Client.
Build-Time-Integration
Die Build-Time-Integration basiert auf der Bereitstellung der Frontends über Pakete, die dann von einer Container-App als Abhängigkeit geladen werden. Dies bedeutet jedoch, dass ich bei einer Änderung an einem Mikro-Frontend alles neu erstellen und bereitstellen muss.
Daher sollte man diesen Ansatz um jeden Preis vermeiden. Immer wenn ein UI-Element geändert wird, müssen alle Produktelemente neu erstellt werden. Auf Code-Basis sind die Dienste gut voneinander getrennt, so dass sie unabhängig entwickelt und getestet werden können. Bei der Build-Time-Integration sind die Dienste jedoch wieder eng miteinander gekoppelt. Deshalb ist es wichtig, eine Architektur zu wählen, die das Frontend erst zur Laufzeit integriert. Webpack5 und Module Federation bieten hier einen spannenden Ansatz, bei dem die Abhängigkeiten erst zur Build-Zeit definiert werden müssen. Die einzelnen Module werden unabhängig voneinander in separaten Deployment Units ausgeliefert. Mehr dazu findet ihr hier. Dieser Ansatz ist unabhängig vom UI-Framework und funktioniert zumindest mit Angular und React stabil, auch wenn Webpack5 nur als Beta- oder RC2-Release verfügbar ist. Diese Lösung hat zwei Vorteile:
Zum einen entfallen alle Nachteile der Build-Time-Integration, zum anderen werden die Abhängigkeiten validiert. Allerdings müssen alle Teams Webpack5 verwenden.
Laufzeitintegration auf dem Server
Eine der einfachsten Lösungen ist die Integration auf dem Server. Frontends, die aus mehreren Komponenten verschiedener Backend-Microservices bestehen, werden vom Server aufgelöst und für den Client gebündelt. Der Client muss dann nur noch eine Anfrage stellen (siehe Abbildung 3). Diese Stärke kommt vor allem in einer Umgebung zum Tragen, in der mit höheren Latenzzeiten zu rechnen ist oder die Verbindungen nicht immer stabil sind. Die Technologien zur Implementierung sind auf Webserver- oder Cache-Ebene mit Server Side Includes (oder Edge Side Includes) verfügbar. Eine Implementierung mit separaten Frameworks (z.B. Tailor) ist ebenfalls möglich.
Server Side Includes (SSI)
SSI ist eine etablierte Technologie, die ihren Ursprung in den 1990er Jahren hat und immer noch von gängigen Webservern (z.B. nginx, Apache, IIS) unterstützt wird. Bei der Betrachtung des Architekturmusters spielt es keine Rolle, ob ihr euch für SSI oder ESI entscheiden, daher verwenden wir für die weitere Betrachtung eine Lösung mit SSI.
Wenn in der vom Client angeforderten Seite weitere Include-Anweisungen enthalten sind, werden diese vom Webserver oder Cache aufgelöst und die referenzierte Seite ist bereits in der Antwort enthalten. Ein großer Nachteil ist, dass der langsamste Dienst die Antwortzeit bestimmt. Das gesamte System kann so ausgelegt werden, dass es im Falle eines Ausfalls schnell reagieren kann. Allerdings ist der Webserver oder Proxy hier der Single Point of Failure und sollte daher schlank und redundant gehalten werden. Auch Querschnittsfunktionen wie Autorisierung oder Authentifizierung können im Proxy realisiert werden.
Auch wenn es derzeit nicht mehr kritisch ist, sollte beachtet werden, dass eine Integration der einzelnen Frontends auch ohne aktiviertes JavaScript im Browser möglich ist. Überlegungen wie eine einheitliche UX/UI werden durch diese Lösung nicht abgedeckt und müssen weiterhin auf organisatorischer Ebene berücksichtigt werden (siehe Tabelle 4).
Diese Variante hat klare Vorteile für Szenarien, in denen es wichtig ist, die Zeit zu minimieren, bis der Endbenutzer etwas im Browser sieht, aber nur, wenn alle Dienste hinter dem Proxy schnell reagieren. Aus diesem Grund ist diese Variante unserer Meinung nach vor allem für B2C- und B2B-Geschäftsszenarien, bei denen die Antwortzeiten kritisch sind, eine Überlegung wert.
Laufzeitintegration auf dem Client
Bei dieser Variante stellt jeder Microservice seine Daten über einen eigenen Request bereit(siehe Abbildung 4). Je nach Verbindung kann dies jedoch negative Auswirkungen haben, wenn alle UI-Komponenten für den Benutzer sichtbar sind. Die Verwendung von iFrames ist der einfachste Weg, um eine Frontend-Integration auf dem Client zu erreichen. Wenn iFrames nicht in Frage kommen, oder wenn ihr auch an eine Verbesserung der UX durch asynchrones Laden und Teilen von Informationen im DOM denkt, sind JavaScript und Ajax oder Frontend-Frameworks, die auf dieser Technologie basieren, eine weitere Option. Darüber hinaus erfreuen sich Web Components (benutzerdefinierte Elemente) dank der immer besseren Browserunterstützung zunehmender Beliebtheit, mit denen die einzelnen Microservices individuelle Widgets bereitstellen können. Diese können dann von anderen Microservices in die GUI eingebettet werden.
Single-page application (SPA)
Eine SPA ist eine Webanwendung, die aus einer einzigen HTML-Seite besteht. Alle erforderlichen Ressourcen und Bibliotheken werden beim ersten Zugriff bereitgestellt. Veränderbare Inhalte werden dynamisch geladen. Dieser Inhalt wird auf dem Client im Browser verarbeitet und die Anzeige wird aktualisiert.
Das bedeutet, dass zu Beginn viel Nutzlast übertragen wird, die dann über Ereignisse weitere Anfragen auslöst. Dies kann z.B. über JSON geschehen. Die Antwort wird verarbeitet und die entsprechende UI wird angezeigt. Bestehende APIs lassen sich ebenfalls leicht integrieren. Mit dem Single-SPA-Framework (mehr dazu) könnt ihr mehrere JavaScript-Frameworks parallel nutzen. Die verschiedenen SPAs werden über eine Konfigurationsdatei, die als Integrationsschicht dient, in eine Gesamtanwendung integriert. Zwar funktioniert nicht mehr alles, wenn JavaScript deaktiviert ist, aber das ist unserer Meinung nach ein kleiner Nachteil. Hinzu kommen weitere Vorteile wie die Skalierbarkeit der einzelnen Teile bei einer hohen Nutzerzahl, die Möglichkeit der Offline-Nutzung und eine gute UX dank Responsivität (siehe Tabelle 5). Plant man den Einsatz mehrerer Frontend-Frameworks und ist die Ladezeit der Gesamtanwendung nicht wirklich kritisch und ist die Anbindung robust genug, um eine höhere Anzahl von Anfragen zu bewältigen, werden die Nachteile leicht kompensiert. Wir haben diese Eigenschaften vor allem in internen Umgebungen gesehen, wo wir dieses Muster bevorzugen würden. Aber auch im B2B-Umfeld sind diese Nachteile je nach Art des Kunden nicht so bedeutend.
Web-Komponenten
Damit könnt ihr wiederverwendbare Komponenten auf der Grundlage von Web-Komponenten-Standards erstellen. Es handelt sich um wiederverwendbare HTML-Komponenten, die browserübergreifend funktionieren. Web Components wurden bereits 2012 vom W3C als Standard veröffentlicht und werden mittlerweile von allen gängigen Browsern unterstützt. Im Wesentlichen bestehen Web Components aus Custom Elements, dem Shadow DOM und HTML-Templates. Der Vorteil von Web Components ist, dass sie unabhängig von den verwendeten Bibliotheken und Frameworks (wie Angular oder jQuery) implementiert werden können. Die Schnittstellen zu anderen Teams bilden dann UI-Komponenten, d.h. die Komposition aus Präsentation, Verhalten und Daten, und nicht nur eine API und Datenobjekte. Für die Integration sind die Bereitstellung der Implementierung und der Zugang zu ihr erforderlich. Die Webkomponente selbst übernimmt die Kommunikation zum Backend.
In Bezug auf die Architektur benötige ich eine Kompositionsschicht, die eine Startseite darstellt und die einzelnen Webkomponenten lädt und anzeigt. Außerdem wird ein Paketmanager benötigt, wie z.B. node. Das sollte aber mittlerweile ein De-facto-Standard in jedem Webprojekt sein. Voraussetzung ist der Einsatz von JavaScript, aber auch dies ist mittlerweile eine triviale Anforderung ohne Nachteil (siehe Tabelle 6).
Bezogen auf das Geschäftsszenario gibt es keine eindeutigen Vor- oder Nachteile für B2B, B2C oder Inhouse-Szenarien. Das Einzige, was man in B2B- und B2C-Zielszenarien im Auge behalten sollte, ist die erhöhte Anzahl von Server-Roundtrips.
Neben der verwendeten Frontend-Variante fassen wir im Folgenden weitere Erfahrungen zusammen.
Integration von Mikro-Frontends in das bestehende IT-Ökosystem
Wenn ein neues System entsteht, stehen immer auch Altsysteme im Kontext. Entweder soll ein Ersatz für ein bestehendes System geschaffen werden oder die neue Funktionalität wird benötigt.
Das Muster für die Integration von Altsystemen in ein Mikro-Frontend-Ökosystem hängt oft vom Alter der zu integrierenden Anwendungen oder von der Architektur und dem Technologie-Stack ab.
Es besteht keine Notwendigkeit, alle Frontend-Funktionalitäten auf den Altsystemen komplett neu zu entwickeln, da oft schon die Bereitstellung kleinerer funktionaler Zusammenhänge ausreicht, um insgesamt einen geschäftlichen Nutzen zu generieren. Zwei mögliche Szenarien für die Integration und Nutzung sind die sukzessive Ablösung und die Anpassung.
Die sukzessive Ablösung der Altsysteme richtet sich nach dem Geschäftswert, den ein Redesign mit sich bringt. Dies erfordert eine parallele Entwicklung und einen parallelen Betrieb in beiden Systemen. Bei der Ablösung wird das Altsystem vorübergehend in das neue System integriert - zum Beispiel durch die Erstellung eines API-Fassadenmusters. Diese Fassade verbirgt das Altsystem, während die Dienste des neuen Systems integriert werden. Funktionen aus dem Altsystem werden genutzt und über die Fassade in den neuen Microservice und die Micro-Frontend-Architektur integriert. Nach und nach werden immer weniger Funktionen des Altsystems genutzt. Für den Benutzer ist es unklar, ob er mit dem neuen System oder mit dem Altsystem arbeitet. Dieses Muster wird als "Strangulierungsmuster" bezeichnet. Wenn ihr mehr über das Ablöseszenario erfahren möchtet, lest ihr hier oder hier: C. Richardson, Microservice Patterns, Manning Publications, 2018.
Bei der Adaption geht es um die Erweiterung des Funktionsumfangs der Altsysteme. Bei diesen Anpassungen kann es sich um technische Erweiterungen handeln, indem beispielsweise der Zugriff über native Apps auf mobilen Geräten erforderlich ist - oder es werden neue Funktionen benötigt, die mit den Legacy-Systemen interagieren müssen. Spezialisierte API-Schichten können hier als Muster verwendet werden, um universelle, umfangreiche API-Schichten zu vermeiden. Diese spezialisierten Schichten werden als "Backend for Frontend"-Schichten bezeichnet. Sie sind auch dann geeignet, wenn die Altsysteme in das neue Mikro-Frontend-Ökosystem integriert werden sollen. Das Altsystem kann dann über zwei Frontends parallel genutzt werden. Mehr zum Thema "Backends für Frontends" findet ihr hier oder hier: S . Newmann, Monolith to Microservices, O'Reilly, 2020.
Bootup-Template
Jede Microservice-Architektur hat wiederkehrende Anforderungen, die jedes Team erfüllen muss, und das gilt auch für Micro-Frontends. Diese sollten nicht völlig unabhängig voneinander gelöst werden, sondern zwischen den Teams abgestimmt werden. Nach unserer Erfahrung müssen die folgenden Funktionalitäten im Gesamtkontext betrachtet und abgestimmt werden (siehe Abbildung 5):
Wir ziehen es vor, Muster bereitzustellen, die einmal entwickelt werden und die dann von anderen Teams verwendet werden können. Kein einzelnes Team ist dann für diese Funktionalitäten verantwortlich. Es ist am besten, die Muster als Vorlage zu betrachten: Jedes Team kann den Umfang erweitern, das Architekturmuster verbessern oder Fehler beseitigen, wie es möchte. Die anderen Teams können dann die angepassten Patterns nutzen.
Die Wartung durch ein zentrales Team muss sorgfältig überlegt und bewertet werden, da dies zu einer horizontalen Abhängigkeit aller Teams führt, die im Sinne der Ziele von Microservices vermieden werden sollte.
Musterbibliothek
Ihr solltet euch auf eine gemeinsame Musterbibliothek einigen (siehe das Beispiel von OTTO hier), um ein gemeinsames Look & Feel zu schaffen. Eine zentrale Bereitstellung ermöglicht es den Microservice-Teams, schneller Ergebnisse zu erzielen. Die Aufgabe besteht darin, einen Weg zu finden, wie dies umgesetzt werden kann. Eine zentrale Verwaltung und Abnahme ist wenig hilfreich; die Teams sollten das Frontend mit Hilfe der Pattern-Bibliothek nach definierten Frameworks selbst aufsetzen können. Es kann sinnvoll sein, eine Gruppe von Usability- und UX-Experten einzurichten: Dieses Team kann sich um Themen wie Nutzerbefragungen, Vergleichsstudien, Usability-Analysen und Usability-Tests sowie ganzheitliche Betrachtungen mit Hilfe von User Journey Maps kümmern. Diese Skalierung ist jedoch auch eine Frage der Größe des Gesamtsystems.
Muster werden zusätzlich zur Erreichung der aus den nicht-funktionalen Anforderungen abgeleiteten Ziele an ihre technologischen Rahmenbedingungen erstellt. Diese haben sich nach unserer Erfahrung als hilfreich und förderlich für die Erreichung der genannten Ziele erwiesen:
Aufgrund ihrer Einfachheit und Robustheit ist die serverseitige Implementierung auf der Grundlage von Server Side Includes sehr oft eine gute Lösung, da es nur wenige Einschränkungen und Anforderungen an alle Teams gibt. Allerdings ist es wichtig, die SSI-Integrationsschicht so schlank wie möglich zu halten und nur die wichtigsten übergreifenden Belange an einer Stelle zu implementieren. Wird die Integrationsschicht zu dominant, besteht die Gefahr von technischen Abhängigkeiten und die Gefahr von kostspieligen Breaking Changes nimmt deutlich zu. Es bleibt abzuwarten, ob sich Webpack5 durchsetzen und eine robuste Lösung bieten wird, die aktuelle Version sieht aber vielversprechend aus. Web Components werden von immer mehr Browsern unterstützt und sind durch einen Standard spezifiziert. Im Großen und Ganzen sind Web Components eine gute Lösung, wenn sich UI-Komponenten als Abstraktionsschicht im Kontext der individuellen Anforderungen eignen.
Ist die Geschäftsdomäne überschaubar und nicht sehr umfangreich, kann dies ein Zeichen dafür sein, dass ich kein Mikro-Frontend benötige: Es kann ausreichen, eine gute Microservice-Architektur für die Funktionalität zu entwerfen und eine übergreifende Frontend-Schicht zu verwenden. In diesem Szenario besteht jedoch die Gefahr, dass die Implementierung eines Mikro-Frontends zu viel Overhead und unnötige Komplexität erzeugt.
Wenn viele Teams das Produkt entwickeln und die Domäne stark fragmentiert ist, kann es sehr vorteilhaft sein, eine Mikro-Frontend-Lösung zu bauen. Micro-Frontends kommen erst dann richtig zum Tragen, wenn der Funktionsumfang sehr groß ist. Die einzelnen Entwicklungsteams können jeweils unabhängig voneinander arbeiten, und die Versprechen der Microservices werden erfüllt. Allerdings ist es wichtig, die technologische Vielfalt der UI-Frameworks im Auge zu behalten und zu minimieren, sonst entsteht eine Art Technologiezoo, der nicht mehr beherrschbar ist.
Es ist davon auszugehen, dass die Anforderungen an Frontends, sowohl in funktionaler als auch in nicht-funktionaler Hinsicht, immer umfangreicher werden. Daher muss die Architektur skalierbar sein. Eine klare Trennung zwischen den Domänen ist notwendig, um ein gutes Gleichgewicht zwischen Kopplung und Kohäsion zu erreichen. Gerade weil die Prinzipien und Praktiken für Microservices etabliert und bekannt sind, ist es ratsam, sie auf die Frontends zu übertragen - aber immer nur in dem Maße, wie es notwendig und angemessen ist. Und dafür ist es unerlässlich, seine funktionalen und nicht-funktionalen Anforderungen nicht nur zu kennen, sondern auch sehr genau zu verstehen.
We have received your feedback.