Kolumne

EnterpriseTales: Was ein „modernes“ Web-Frontend bedeutet

Sven Kölpin
Funktionale Programmierung

© S&S_Media

Der Entscheidungsprozess bei Web-Frontends ist häufig Framework-getrieben und betrachtet die architektonischen Implikationen nur am Rande. So ist es heute schon fast selbstverständlich, dass Webanwendungen als clientseitig gerenderte Single Page Applications (SPA) entwickelt werden. Die allgemeine Definition eines „modernen“ Frontends ist dabei nicht selten unmittelbar an die Auswahl eines konkreten Frameworks gebunden. Die Zeiten der eher klassischen, rein serverseitig getriebenen Ansätze (z. B. JSF) scheinen vorbei zu sein.

SPAs haben durchaus ihre Vorzüge, nicht umsonst ist dieses Konzept langsam, aber sicher zum Status quo der heutigen Webentwicklung geworden. Man darf allerdings die Augen nicht davor verschließen, dass die Entwicklung rein clientseitiger Webanwendungen bedeutende Auswirkungen auf die Systemarchitektur und viele neue Herausforderungen mit sich bringt.

Die unterschiedlichen Ansätze

Der Unterschied zwischen serverseitigen Ansätzen und den rein clientseitigen Frameworks liegt darin, an welcher Stelle die eigentliche Logik beziehungsweise der Status einer Webanwendung verwaltet wird. Dazu gehören unter anderem die UI-Logik, die Renderlogik und zu gewissen Teilen auch die Businesslogik einer Applikation. In serverseitig gerenderten Anwendungen wie JSF wird der Großteil der Daten auf dem Server verarbeitet. Bei jedem Request generiert das Framework dynamisch HTML auf Basis des aktuellen Zustands der Anwendung. Der Server ist also die einzige Quelle der Wahrheit (Single Source of Truth) und der Client (Browser) nimmt häufig eine eher passive Rolle ein. JavaScript kommt allenfalls für simple DOM-Manipulationen oder für die Verarbeitung asynchroner Anfragen (AJAX) zum Einsatz.

Bei clientseitigen Webanwendungen ist das anders. Große Teile der Logik und des Status, dazu zählt vor allem die UI- und Renderlogik, wird clientseitig ausgeführt. Deshalb spricht man hier auch von clientseitig gerenderten Anwendungen. Sowohl der Server als auch der Client nehmen bei dieser Art von Webanwendung eine andere Rolle ein. Durch die Verschiebung UI-spezifischer Logik in den Client gewinnt der Browser an Bedeutung. Er fungiert nun nämlich als Laufzeitumgebung für eine echte JavaScript-Anwendung und nicht mehr nur als „dummer“ Client. Der Server hingegen gibt einen Großteil der Verantwortung für die Benutzungsoberfläche ab und wird so vornehmlich als Datenlieferant verwendet.

Für die Entwicklung heißt das im Klartext, dass clientseitig gerenderte Webapplikationen in zwei deutlich getrennte Anwendungsteile, nämlich das Frontend und Backend, aufgeteilt sind. Anders als bei rein serverseitigen Frameworks, wie zum Beispiel JSF, kann somit nicht mehr eine zentralistische Webanwendung, die durch eine entsprechende Abstraktion häufig sogar zu großen Teilen in nur einer Programmiersprache (z. B. Java) entwickelbar ist, implementiert werden. Das hat neben technischen und architektonischen vor allem auch organisatorische Auswirkungen. Beispielsweise werden durch die Trennung häufig automatisch wieder dedizierte Backend- und Frontend-Teams gebildet.

Clientseitiges vs. serverseitiges Rendering

Das clientseitige Rendern von Webanwendungen bietet Vorteile in verschiedenen Bereichen. In erster Linie profitieren die Konsumenten einer solchen Applikation. Durch die Verlagerung von Status und Logik in den Browser müssen weniger Abgleiche mit dem Server vorgenommen werden – das bedeutet im Umkehrschluss weniger Netzwerklast und somit im Allgemeinen ein flüssigeres Erlebnis für den Benutzer. Die durch das clientseitige Rendering verbesserte Interaktionsfähigkeit der Weboberfläche ist häufig das, was von vielen als „modern“ empfunden wird und einer der Hauptgründe für den Erfolg von clientseitig gerenderten Webanwendungen.

W-JAX 2019 Java-Dossier für Software-Architekten

Kostenlos: Java-Dossier für Software-Architekten 2019

Auf über 30 Seiten vermitteln Experten praktisches Know-how zu den neuen Valuetypes in Java 12, dem Einsatz von Service Meshes in Microservices-Projekten, der erfolgreichen Einführung von DevOps-Praktiken im Unternehmen und der nachhaltigen JavaScript-Entwicklung mit Angular und dem WebComponents-Standard.

 

Als weiterer Vorteil wird zudem häufig die Skalierbarkeit genannt. Durch die Verlagerung der UI-, Render- und Businesslogik in den Client werden serverseitig weniger Ressourcen benötigt. Schließlich müssen keine CPU-Zyklen mehr für die Generierung von HTML durch die Templating Engines (z. B. Facelets bei JSF) aufgewendet werden. Auch Session-Informationen liegen nicht mehr zwingend auf dem Server, was bei großen Benutzerzahlen positive Auswirkungen auf den Speicherverbrauch haben kann. Vor allem im Cloud-Umfeld, in dem die serverseitige Statuslosigkeit durch Prinzipien wie Function as a Service (FaaS) eine zentrale Rolle eingenommen hat, bringt das clientseitige Rendering somit entscheidende Vorteile.

Es darf aber nicht vernachlässigt werden, dass eine Verschiebung des Renderns in den Browser auch negative Auswirkungen auf das Erlebnis der Benutzer haben kann. Beim Besuch „moderner“ Webanwendungen wird man beispielsweise häufig mit einer Vielzahl von Ladebalken und Platzhaltern begrüßt, bis schließlich nach und nach der eigentliche Inhalt der Seite aufpoppt. Das ist dadurch begründet, dass ein Server beim clientseitigen Rendering initial lediglich den reinen JavaScript-Code, jedoch noch keine Daten ausliefert. Diese müssen nach dem ersten Rendern durch zusätzliche Serveranfragen nachgeladen werden. Die Zeit, die vergeht, bis ein Nutzer aktiv mit einer clientseitigen Applikation interagieren kann (Time to Interactive), ist deshalb oftmals höher als bei serverseitigen Anwendungen. Das ist im Übrigen einer der Gründe dafür, warum es nicht unüblich ist, die eigentlich clientseitig gerenderten Anwendung auf dem Server zu generieren (z. B. mit Next.js) und so einen Hybrid aus serverseitig und clientseitig gerenderter SPA auszuliefern. Damit kann zwar der initiale Seitenaufbau beschleunigt werden, allerdings erfordert das Vorgehen zwingend die serverseitige Ausführung von JavaScript (z. B. mit Hilfe von Node.js), mehr serverseitige Ressourcen und erhöht die Komplexität eines Systems.

Clientseitige Architektur

Clientseitig gerenderte SPAs sind vollwertige, eigenständige Anwendungen und brauchen deshalb, genau wie Backend-Anwendungen, eine wohldefinierte Architektur und klare Regeln. Anders als bei mit serverseitigen Frameworks entwickelten Webanwendungen, in denen JavaScript zumeist allenfalls als ein Nebenprodukt zu bezeichnen ist, fällt hier eine große Menge clientseitiger Code (JavaScript oder TypeScript) an. Architektonische Grundprinzipien aus der Backend-Welt sollten deshalb zwingend auch clientseitig Verwendung finden. Die meisten der grundsätzlichen architektonischen Entscheidungen sind dabei gar nicht an eine konkrete technische Umsetzung gekoppelt und deshalb weitestgehend Framework-unabhängig. Dazu zählen triviale Dinge wie eine einheitliche Namensgebung (z. B. Schnittstellen zur serverseitigen API enden mit Repository o. Ä.) oder eine wohldefinierte Organisation der Source-Dateien (z. B. fachlich geschnittene Ordnerstrukturen). Im Idealfall sind hier Ansätze zu wählen, die sich nah an den Konzepten aus dem Backend bewegen und so eine teamübergreifende Orientierung in beiden Welten ermöglichen.

Das Ziel einer Architektur im Frontend muss zudem sein, die Fachlichkeit bzw. Anwendungsfälle der Applikation in den Fokus zu rücken und die Kopplung zu etwaigem Framework-Code so lose wie möglich zu halten. Die Regeln bekannter Architekturkonzepte, wie sie beispielsweise aus Domain-driven Design (DDD) oder Clean Architecture bekannt sind, lassen sich dazu auch im Frontend verwenden. Vor allem in der schnelllebigen JavaScript-Welt ist eine wohldefinierte Architektur eine Art von Versicherung für die Zukunftsfähigkeit der Anwendung.

Clientseitig gerenderte SPAs müssen außerdem in der Lage sein, Daten mit verschiedenen Quellen und Lebenszyklen zu verwalten. Schließlich laufen auf einer Benutzungsoberfläche viele Anwendungsfälle zusammen, die auf gemischten Datenquellen basieren und sich häufig gegenseitig beeinflussen. Das State Management ist deshalb eine der wichtigsten und gleichzeitig komplexesten Aufgaben einer clientseitigen SPA. Technisch gesehen wird diese Herausforderung zwar mittlerweile mit Hilfe von Konzepten wie unidirektionalen Datenflüssen weitestgehend verstanden, doch für ein skalierbares State Management muss vor allem eine fachliche Definition des zu verwaltenden Status in den Vordergrund gerückt werden. Dazu gehören beispielsweise die Definition der Lebensdauer von Daten bestimmter Use Cases (z. B.: Wann bzw. wie häufig werden Daten vom Server aktualisiert?), die Deklaration von Abhängigkeiten zwischen verschiedenen Anwendungsfällen (z. B.: Wenn sich A ändert, müssen sich B und C gleichzeitig aktualisieren.) sowie das Festlegen von Datenquellen für die einzelnen Funktionalitäten (z. B.: Was sind lokaler Status, Status des Servers, Status aus der URL, Status des LocalStorage, …?).

Wo kommen die Daten her?

Eine clientseitige Webanwendung kommuniziert zumeist über eine auf JSON basierende HTTP-Schnittstelle mit dem Server. Häufig werden diese APIs allerdings datenzentriert geschnitten. Zwar kann so eine höhere Wiederverwendbarkeit der Schnittstelle geschaffen werden, spätestens bei komplexeren Anwendungsfällen hat ein datenzentriertes API aber entscheidende Nachteile. Beispielsweise werden zur Umsetzung eines Use Cases häufig Daten aus vielen verschiedenen Quellen gleichzeitig benötigt. Bei der Abbildung des Anwendungsfalls in der clientseitigen Applikation kann das schnell zu vielen Requests führen, die wiederum jeweils viele, eigentlich gar nicht benötigte Daten beinhalten.

Idealerweise sollten serverseitige Schnittstellen für clientseitig gerenderte Webanwendungen deshalb anwendungsfallzentriert sein, um so genau die für die jeweiligen Use-Cases benötigten Daten liefern zu können. Eine Möglichkeit für die Umsetzung solcher Schnittstellen ist beispielsweise das Backend-for-Frontend-(BFF-)Pattern. Die Idee dieses Konzepts ist, dass jeder Client (also auch eine SPA) sein eigenes, speziell auf die jeweiligen Use Cases und Endgeräte zugeschnittenes Backend (bzw. API) erhält. Ein Frontend (bzw. das Team dahinter) ist der Besitzer des Backends und kann somit eigenständig auf Änderungen von Anforderungen reagieren und passende Technologien einsetzen (z. B. Node.js bei SPAs). Auf der anderen Seite führt das Konzept aber auch zu neuen Herausforderungen, wie etwa einer erhöhten Komplexität durch mehr Infrastruktur sowie einer Duplizierung von Logik.

Als zweite Variante für die Umsetzung von Schnittstellen für clientseitige Anwendungen bieten sich datenzentrierte APIs an, die die Möglichkeit bieten, anwendungsfallbezogene Abfragen zu bedienen. Die bekannteste Implementierung davon ist GraphQL. Die von Facebook entwickelte Abfragesprache ermöglicht das Formulieren spezieller Queries, in denen genau angegeben werden kann, welche Daten bzw. welche Sichten auf bestimmte Daten benötigt werden. Somit lässt sich das Problem von zu vielen Requests oder von zu vielen Daten elegant umgehen. GraphQL ist selbstverständlich auch mit dem BFF-Pattern kombinierbar.

Die Implementierung geeigneter Schnittstellen für eine clientseitige Applikation ist häufig alles andere als trivial und sollte nicht als Nebenprodukt einer SPA gesehen, sondern als zentraler Bestandteil der Gesamtarchitektur im Detail betrachtet werden.

Weitere Besonderheiten

Beim clientseitigen Rendern gibt es viele weitere Herausforderungen, die bei den rein serverseitigen Anwendungen bereits weitestgehend gelöst wurden. So sind die Themen Validierung, Autorisierung, Logging und Internationalisierung als einige von vielen Besonderheiten zu nennen. Dabei liegt die Kunst vornehmlich darin, die Duplikation fachlicher Logik zu minimieren und eine Balance zwischen benötigten serverseitigen Informationen und der Menge an Requests zu finden.

Als Faustregel gilt, dass nichts im Client sicher ist. Man muss sich also stets vor Augen halten, dass (zumindest theoretisch) der gesamte clientseitige Code jeder Zeit von Dritten ausgelesen und manipuliert werden kann. Etwaige Validierungs- oder Autorisierungsmechanismen müssen deshalb mindestens serverseitig greifen. Clientseitige Überprüfungen sind allenfalls als nette Hilfestellungen, aber niemals als Sicherheitsfeatures zu verstehen. Für die Minimierung von Codeduplizierung, beispielsweise für Validierungsregeln, kann unter anderem auf Universal JavaScript, also die Verwendung von JavaScript auf der Client- und Serverseite, zurückgegriffen werden. Das setzt aber natürlich das Vorhandensein einer serverseitigen JavaScript Engine voraus (z. B. Node.js).

Für Themen wie Logging und Internationalisierung sollte vor allem das Einsparen von Requests oberste Priorität haben. Aggressive Caching-Strategien oder das serverseitige Rendern von Daten im HTML sind beispielsweise Möglichkeiten, die die Geschwindigkeit und Datenmengen einer SPA optimieren können. Logging hat bei clientseitig gerenderten Applikationen eine hohe Priorität. Schließlich lassen sich mit detaillierten Logmeldungen Probleme frühzeitig erkennen oder Benutzerverhalten analysieren. Anders als bei serverseitigen Frameworks gilt es aber, vor allem auf die Häufigkeit und Größe der Logmeldungen zu achten, da diese über das Netzwerk an den Server übertragen werden müssen. Es gibt hierbei verschiedene Logging-Strategien, die je nach Applikation oder Anwendungsfall zu wählen sind (z. B. direktes Logging im Fehlerfall, zeitbasiertes Logging, Event-basiertes Logging).

Fazit

Clientseitig gerenderte Single Page Applications erfordern weitaus mehr als nur die Auswahl des Frameworks. Durch das Verschieben von Daten und Logik in den Client, ergeben sich neue Herausforderungen, die es bei rein serverseitigen Frameworks so nicht gibt. Selbstverständlich haben clientseitige SPAs viele Vorteile gegenüber den klassischen Ansätzen, vor allem in Bezug auf Geschwindigkeit, Skalierbarkeit und Interaktionsmöglichkeiten. Bei der Evaluation eines Frontends sollten diese Vorteile aber bewusst dem potenziellen Mehraufwand und der zusätzlichen Komplexität einer clientseitig rendernden Anwendung gegenübergestellt werden.

Unabhängig von den hier diskutierten Herausforderungen, gibt es in der Java-Welt mit Frameworks wie Vaadin auch die Möglichkeit, clientseitig gerenderte Anwendung mit reinem Java-Code zu entwickeln. Vor allem in Teams ohne tiefgehende JavaScript- oder TypeScript-Erfahrung ergibt die Evaluation solcher Ansätze durchaus Sinn.

Zuletzt lässt sich festhalten, dass es grundsätzlich auch mit vermeintlich ausgedienten Frameworks wie JSF möglich ist, „moderne“, aber serverseitig gerenderte SPAs zu entwickeln – die Verwendung rein serverseitiger Frameworks ist also durchaus auch im Jahr 2019 noch in Ordnung.

In diesem Sinne: Stay tuned.

Geschrieben von
Sven Kölpin
Sven Kölpin
Sven Kölpin ist Enterprise Developer bei der open knowledge GmbH in Oldenburg. Sein Schwerpunkt liegt auf der Entwicklung webbasierter Enterprise-Lösungen mittels Java EE.
Kommentare

Hinterlasse einen Kommentar

1 Kommentar auf "EnterpriseTales: Was ein „modernes“ Web-Frontend bedeutet"

avatar
4000
  Subscribe  
Benachrichtige mich zu:
Irgendwer
Gast

„Die Zeiten der eher klassischen, rein serverseitig getriebenen Ansätze (z. B. JSF) scheinen vorbei zu sein.“

Unsinn!