Blog

Single Page Application

Nachhaltige Webentwicklung: Warum bauen wir Single-Page Applications und was kommt als nächstes?

Eine Single-Page Application ist in vielen Unternehmen die Standardlösung für Benutzeroberflächen im Web. Es wird heiß über das „richtige“ Framework diskutiert, nicht aber, ob eine SPA überhaupt der richtige Architekturansatz für das konkrete Projekt ist. Wie sind wir an den Punkt gelangt, an dem wir blind diesen Weg einschlagen und damit unbewusst eine ganze Reihe von Kompromissen in Kauf nehmen? Gibt es andere Wege? Und wohin führt das alles? 

Auf seinem Vortrag auf der Seacon 2021 hat sich Marvin Luchs mit genau diesen Fragen beschäftigt. Da er lieber Code als Texte schreibt, hat er direkt, nachdem der Applaus verhallt ist, seine Notizen vertrauensvoll weitergereicht. Ich habe seine Erkenntnisse kompakt zusammengefasst, sodass sie trotzdem ihren Weg in die schriftlichen Chroniken des Internets finden.  (Wer keine Lust hat zu lesen, findet den Videolink am Ende des Beitrags.)

Nachhaltige Webentwicklung: Evolvierbarkeit und Interoperabilität 

Seinen ersten Kontakt mit Webentwicklung hatte Marvin bereits vor 25 Jahren, als er mit FrontPage 1.1 eine Website mit Detektivtipps aus der Micky Maus zusammenklickte. Die Seite gibt es leider nicht mehr. Aber zur gleichen Zeit lief im Kino Space Jam. Die Website zum Film ist heute noch online und lässt sich auf Geräten aufrufen, die es zur damaligen Zeit in der Form noch gar nicht gab. 

Diese Nachhaltigkeit unterscheidet das Web von vielen anderen Plattformen. Dass das so ist, ist kein Zufall: Die wesentlichen Architekturmerkmale des Webs sind Evolvierbarkeit und Interoperabilität. Sie ziehen sich wie ein roter Faden durch die Standards und Technologien des Webs. Und diese Merkmale machen das Web zu einer äußerst attraktiven Applikationsplattform. 

Informierte Architekturentscheidungen statt trendgetriebene Technologieauswahl 

Um auf dieser Plattform informierte Architekturentscheidungen zu treffen und nachhaltig zu entwickeln, ist es erforderlich, sich mit ihren Eigenschaften auseinandersetzen. In den 2000ern galt Flash als „modern“ und war der einfache Weg, um interaktive und multimediale Inhalte ins Web zu bringen. Für FilmWebsites war das vermutlich die richtige Technologie zur richtigen Zeit. An ein Workflow-Management-System werden hingegen ganz andere Anforderungen gestellt. Natürlich soll es ein „modernes“ User Interface bekommen, aber wesentlich wichtiger ist die Langlebigkeit der Anwendung. Die Investition, eine solche Software zu entwickeln, muss sich lohnen. Flash wäre vermutlich schon damals nicht die richtige Technologie für diesen Anwendungsfall gewesen. 

Heute suggerieren Twitter und Medium, dass wir nicht mehr „modern“ sind, weil wir immer noch Webpack nutzen, unser CSS nicht in JavaScript schreiben oder weil wir unsere REST-API noch nicht durch GraphQL ersetzt haben. Es entsteht der Eindruck, dass man etwas falsch macht, wenn man technologisch nicht mitzieht. 

Es ist die Aufgabe der Softwarearchitekt*innen, geeignete Lösungen für unsere Anforderungen zu entwerfen – nicht den letzten Hypes, Trends und absoluten Wahrheiten zu folgen. Wenn wir die Eigenschaften der Plattform und der Technologien verstehen und diese mit den konkreten Projektanforderungen abgleichen, dann können wir informierte Technologieentscheidungen treffen. 

Wenn heute die Wahl nur noch zwischen Angular oder React gefällt wird (oder eins von beiden sogar als bereits „gesetzt“ im Unternehmen gilt), dann geschieht das Gegenteil: Trendgetriebene Technologieauswahl führt zu impliziten Architekturentscheidungen – in diesem Fall die Entscheidung, eine Single-Page Application zu entwickeln. 

„Klassische Webanwendungen“ mit Server-Side Rendering

Die Evolutionsschritte, die uns von „klassischen Webanwendungen“ zur Single-Page Application bringen, hat Marvin mit Hilfe von fünf Implementierungen einer einfachen To-Do-App nachvollzogen. Der Funktionsumfang ist überschaubar: Über ein Formular kann man Aufgaben hinzufügen und optional ein Fälligkeitsdatum festlegen. Die Werte werden bei der Eingabe direkt validiert. Und wenn eine Aufgabe als erledigt markiert wurde, erscheint sie mit Zeitangabe in der Liste der erledigten Aufgaben.

Screenshot der To-Do-App in ihrer ersten Version.
Die To-Do-App in ihrer ersten Version: eine klassische Webanwendung.

In ihrer ersten Version ist die App das, was gemeinhin als „klassische Webanwendung“ bezeichnet wird. Sie funktioniert so, wie das Web schon seit fast 30 Jahren funktioniert: Der Client schickt eine Anfrage an den Server und bekommt als Antwort eine HTML-Seite. Man interagiert mit der Seite über Links und Formulare und um den Inhalt der Seite zu ändern, muss die Seite neu geladen werden. 

Das Spannende aus heutiger Sicht: Die Anwendung kommt komplett ohne JavaScript aus. Ihre Kernfunktionalität lässt sich also ohne client-seitiges Scripting umsetzen. Auf wie viele Anwendungen, gerade im Enterprise-Umfeld, trifft das wohl zu? Natürlich ist das Ziel nicht, komplett auf JavaScript zu verzichten. Aber es ist auch nicht so unverzichtbar, wie häufig angenommen wird. 

Nun, da die grundlegende Funktionalität der Anwendung implementiert ist, widmen wir uns dem Feinschliff. Aus Benutzersicht gibt es ein paar Punkte, die verbesserungswürdig sind: Jede Änderung am Inhalt der Seite erfordert einen Page-Reload, das Formular begrüßt mich direkt mit Validierungs-Styling und das Erledigen von Aufgaben fühlt sich ungelenk an. 

Mit nur 34 Zeilen JavaScript werden in der zweiten Version der To-Do-App die letzten zwei Punkte aus der Welt geräumt. Damit schlägt die Validierung erst an, wenn ich etwas in das Formular eingebe und die Aufgabenliste wird automatisch abgeschickt, wenn ich eine Aufgabe erledige. Auf Serverseite musste für diese Verbesserungen nichts ändert werden. Die ausgelieferte Seite wird mit Hilfe von JavaScript clientseitig verbessert: Das ist Progressive Enhancement

Mit etwas mehr JavaScript vermeiden wir in der dritten Version zusätzlich den Seitenreload. Auch hierfür musste am Server nichts geändert werden. Das Submit-Event der Formulare wird per JavaScript abgefangen und die Daten im Hintergrund asynchron ans Backend gesendet werden.  Der Server antwortet wie immer mit einer HTML-Seite. Die geänderten Seiteninhalte werden aus der Antwort extrahiert und die entsprechenden Stellen im DOM dynamisch ausgetauscht. Wird JavaScript im Browser deaktiviert oder es tritt ein Fehler auf, funktioniert die Seite exakt wie in der ersten Version. 

Aus Sicht des Benutzers gibt es an dieser Stelle nicht mehr viel zu beanstanden. Die App erfüllt ihren Zweck und die Benutzerinteraktion ist angemessen unkompliziert und schnell. Aus technischer Sicht könnte man allerdings bemängeln, dass HTML-Seiten als Datenformat nicht sonderlich effizient sind.

Server- oder clientseitiges Rendering?

Was passiert, wenn Frontend und Backend stattdessen über JSON kommunizieren? Die vierte Iteration der To-Do-App setzt genau das um. Dafür wird der Server, zusätzlich zur bestehenden HTML-API, um eine JSON-API erweitert. Die HTML-Seite wird nach wie vor vom Server gerendert. Wird nun ein Formular abgeschickt, wandelt der Client die Formulardaten in JSON um und schickt sie an den Server. Die JSON-Antworten und Fehlermeldungen des Servers werden dann mit Hilfe von Templates in HTML übersetzt. Zusätzlich zum Server rendert nun also auch der Client HTML.

Rendering auf Client- und Server-Seite bedeutet auch mehr Code.

Im Ergebnis hat sich der JavaScript-Code der Anwendung so fast verdoppelt. Vieles existiert sowohl im Client als auch im Server: die Template-Rendering-Logik, Error-Binding im Formular und die Datumsformatierung. Das bedeutet auch, dass Änderungen nun häufig an zwei Stellen vorgenommen werden müssen. Dabei muss darauf geachtet werden, dass die Implementierung sich identisch verhält. Und der/die Frontend-Entwickler*in arbeitet dabei permanent in „zwei Welten“. 

Ist das jetzt wirklich eine Verbesserung? Warum nicht alles, was das Rendering betrifft, an einer Stelle, in einer Technologie bündeln und so für eine saubere Trennung von Client und Server sorgen? Et voilà, die Single-Page Application ist geboren.

Rien ne va plus ohne JavaScript 

Bei einer Single-Page Application liefert der Server nur noch eine leere HTML-Seite aus, die anschließend per JavaScript mit Leben gefüllt wird. Sogar Seitenwechsel finden nun ausschließlich im Client statt, indem mit Hilfe eines SPA-Frameworks der Inhalt der Seite dynamisch verändert wird. Indem sämtliche Rendering-Logik in den Client verschoben wird, kann der Server ein ganzes Stück vereinfacht werden. Auch doppelte Implementierungen werden so reduziert. 

Das bedeutet aber auch, dass der Client mehr Verantwortung übernimmt. Nicht nur Logik, die bisher im Server implementiert war (Datumsformatierung, Template-Rendering), liegt nun im Client. Auch Logik, die uns das „herkömmliche“ Web schenkt (Routing, adressierbare Seiten, State Management), muss nun im Client nachimplementiert werden. Ohne JavaScript geht jetzt nichts mehr – nicht einmal die eigentlich statische Liste der erledigten Aufgaben. 

Anderseits hat die Einführung eines SPA-Frameworks die Developer Experience im Frontend wesentlich attraktiver. TypeScript, Code-Minification, DOM-Manipulation – vieles wird einem ohne Konfigurationsaufwand geschenkt oder zumindest erheblich vereinfacht. All das Tooling ist aber auch notwendig, da der Client ist sowohl was die Implementierung als auch das Setup angeht, wesentlich komplexer geworden ist. Unzählige Bibliotheken und Technologien müssen jetzt reibungslos zusammenarbeiten, damit die To-Do-App funktioniert. 

Zusätzlich ist der Ressourcenbedarf gestiegen: zur Entwicklungszeit durch das komplexere Buildsetup, zur Ladezeit, weil mehr Code über die Leitung geht und zur Laufzeit, weil all das JavaScript im Client auch ausgeführt werden muss. Das mag bei einer trivialen Anwendung wie dieser hier nicht ins Gewicht fallen, in größeren Applikationen macht sich da aber schnell bemerkbar. 

Im Ergebnis ist der Client jetzt eine mehr oder weniger eigenständige Applikation, die nur noch sehr lose mit dem Backend über eine JSON-API gekoppelt ist. Das kann von Vorteil sein, wenn man das Frontend unabhängig deployen, unabhängig skalieren oder organisatorisch vom Backend trennen will. Aber ist lose Kopplung hier überhaupt wünschenswert? Wird hier nicht technisch getrennt, was fachlich eigentlich zusammengehört? Wenn neue Daten im Frontend anzeigt oder bearbeitet werden sollen, dann hat das immer auch Änderungen im Backend zur Folge. Wäre da nicht eher hohe Kohäsion wünschenswert? 

Technologieverteilung in den verschiedenen Implementierungen der To-Do-App.

Blickt man auf die verschiedenen Versionen der To-Do-App zurück, dann stellt man fest: Der User war schon mit der dritten Version zufrieden. Danach wurde die Gesamtanwendung zwar wesentlich komplexer, der Endanwender hat davon aber wenig mitbekommen. Für den Entwickler mögen dadurch Vorteile entstanden sein, für den User aber wurde kein Mehrwert geschaffen. 

Warum bauen wir Single-Page Applications?

Das Beispiel zeigt, dass die SPA als Architekturansatz nicht der omnipotente Heilsbringer ist, als welche sie häufig wahrgenommen wird. Also, warum bauen wir Single-Page Applications? Oftmals gelten Single-Page Apps als Voraussetzung für ein modernes User Interface. Aber wie interaktiv ist das Benutzerinterface? Bauen wir gerade wirklich das nächste YouTube? Oder doch eher eine Genehmigungssoftware, die E-Mails mit Excel-Anhängen ablösen soll? Eine Applikation, die primär Formulare, Tabellen und Text darstellt, hat nicht dieselbe Dynamik wie eine Social-Media-Plattform. Filter und Sortieren von Tabellen oder Formularvalidierung rechtfertigen noch keine Single-Page Application. 

Oft nehmen aber organisatorische Rahmenbedingungen auch eine Entscheidung vorweg: Konzernvorgaben, existierendes Knowhow im Unternehmen oder die organisatorische Trennung von Frontend- und Backendentwickler*innen können valide Argumente sein. Es kann also im Sinne des Unternehmens sein, im konkreten Projekt Kompromisse einzugehen. Andererseits sollte ein Unternehmen aber auch für Rahmenbedingungen sorgen, die Projekten zum Erfolg verhelfen. Deswegen lohnt es sich, im Zweifelsfall vermeintliche Wahrheiten zu hinterfragen. 

Letztendlich sollte keiner der Gründe allein ausschlaggebend sein. Es die Aufgabe von Softwarearchitekt*innen, verschiedene Anforderungen, Interessen und Argumente gegeneinander abzuwägen, Kompromisse zu erarbeiten und dann eine informierte Technologieentscheidung zu treffen. Mit einer Konzernvorgabe für ein bestimmtes SPA-Frameworks als Standard für alle Projekte überspringe ich diesen Prozess. 

Und was kommt als nächstes? 

Die Diskussion um Frontendarchitektur wird wieder vielfältiger. Mit dem Ansatz von Single-Page Applications sind wir von einem Extrem ins andere gependelt. Was also, wenn nun die Korrektur einsetzt und das Pendel wieder ein wenig zurückschwingt? Müssen Frontend-Entwickler*innen auf die Developer Convenience verzichten, die React & Co. Liefern? Glücklicherweise gibt es eine Welt zwischen den Extremen „Server-Side Rendering“ und „Client-Side Rendering“ und der Einsatz von React oder Vue.js muss nicht gleichbedeutend sein mit ausschließlich clientseitigem Rendering. 

Server-Side
Rendering
Static SSRSSR with
(Re)Hydration
CSR with
Prerendering
Full Client-Side
Rendering
Server App, die Navigationsanfragen mit HTML-Seiten beantwortet.Single-Page App, die zur Erzeugung statischer HTML-Seiten zur Build-Zeit ausgeführt wird.Single-Page App, die für initiales Rendering serverseitig ausgeführt wird. Anschließend übernimmt der Client.Single-Page App, bei der die App<<< Shell zur Build-Zeit statisch erzeugt wird.Single-Page App, die ausschließlich clientseitig ausgeführt wird.
RenderingDynamisches HTMLStatisches HTMLDynamisches HTML
und JS/DOM
Statisches HTML und JS/DOMNur JS/DOM
ServerrolleServer rendert HTML für jeden Seitenaufruf.Server liefert statisches HTML für jeden Seitenaufruf aus.Server rendert HTML für den ersten Seitenaufruf.Server liefert statisches HTML für de ersten Seitenaufruf aus.Server liefert leeres HTML zum Laden von JS/CSS für de ersten Seitenaufruf aus.
Von SSR bis CSR: die ganze Bandbreite des Renderings im Web.

Bei Static Server-Side Rendering baue ich meine Anwendung mit einem SPA-Framework, das ich aber zur Buildzeit ausführe und so statische HTML-Seiten erzeuge. Das kann sinnvoll sein, wenn ich viele statische Seiten produziere, die sich nur selten ändern (wie beispielsweise in einem Content Management System). Bei SSR mit Rehydration wird die SPA beim initialen Request serverseitig ausgeführt. Erst danach übernimmt der Client das weitere Rendering. Das eignet sich beispielsweise, um den initialen Seitenaufbau einer SPA zu beschleunigen oder zur Verbesserung der Suchmaschinenfreundlichkeit. In einer sechsten Version der To-Do-App wird dieser Ansatz umgesetzt. Bei CSR mit Prerendering wird nicht die ganze Seite clientseitig gerendert, sondern nur die Teile, die wirklich dynamisch sind. Die statischen Teile einer Seite müssen also nicht immer vom Client produziert werden. Auch das erhöht die wahrgenommene Performance der Anwendung. Wer sich für die Vor- und Nachteile der verschiedenen Ansätze interessiert, der sollte den Artikel Rendering on the Web von Jason Miller und Addy Osmani lesen.

Viele der Lösungen für diese Ansätze kommen mit hervorragendem Tooling daher. Sie erfordern allerdings häufig, dass Frontend-Technologien auch auf Server-Seite ausgeführt werden. In einigen Konzernen ist das noch immer noch ein Problem. Auch hier gilt: Gerne mal den Status Quo in Frage stellen. 

Der Einsatz von SPA-Frameworks ist also nicht gleichbedeutend mit einer vollständig clientseitig gerenderten Single-Page App. Die Wahl des Frameworks beeinflusst aber die Optionen in diesem Bereich. Daher muss man zunächst betrachten, was eigentlich gebaut werden soll, um dann zu entscheiden, ob ein Framework überhaupt notwendig ist und falls ja, welches zu eurem Architekturansatz passt. 

Noch Fragen? Marvins kompletten Vortrag von der Seacon 2021 gibt es hier noch einmal zum anschauen, inklusive eines 30-minütigen Q&A am Ende des Vortrags.

Über Marvin Luchs 

Als Kind des Internets ist er ein leidenschaftlicher Verfechter von Webstandards, nachhaltigen Frontend-Architekturen und plattformunabhängigen Lösungen.  Er freut sich über inhaltliche Fragen oder Anregungen zum Beitrag. Rechtschreibfehler dürfen gerne an die Autorin gesendet oder wahlweise behalten werden. Alle Demos der App-Varianten und der zugehörige Code finden sich in seinem GitHub-Repository

Holisticon AG — Teile diesen Artikel

Über den Autor

Textaffine Wortakrobatin, die den Spagat zwischen kreativen Ideen und pragmatischen Lösungen liebt. Turnt daher mit viel Freude im Marketing-Team.

Antwort hinterlassen