Das Frontend ist seit jeher ein wichtiger Bestandteil der meisten Anwendungen. Mit dem Aufschwung von Single Page Applications (SPA) hat sich die Komplexität jedoch im Gegensatz zu Ansätzen, bei denen das gesamte HTML im Backend erzeugt wird, enorm erhöht. Sofern die Wahl auf eine SPA als Frontend-Lösung fällt, steht natürlich sofort eine wichtige Frage im Raum: Wie kann man als Entwickler die neue Komplexität im Frontend bändigen?
Am einfachsten natürlich, indem man nicht auf SPAs setzt. Denn sind SPAs überhaupt immer die richtige Wahl? Sind die bewährten Technologien wie Spring MVC, JavaServer Faces oder gar JavaServer Pages nicht völlig ausreichend, um ein bisschen HTML im Browser anzuzeigen? Die Frage lässt sich natürlich nicht allgemein beantworten, da jede Anwendung individuelle Anforderungen hat, die sich entweder besser mit einer SPA oder eben mit eher klassischen Ansätzen umsetzen lassen. Eine gute Heuristik ist jedoch: Je interaktiver die Anwendung, desto eher lohnt der Einsatz einer SPA. Für weniger interaktive Anwendungen reichen meist obige Technologien, gepaart mit einem kleinen Anteil JavaScript für die interaktiven Bereiche.
Die Versuchung ist groß, pauschal auf den Einsatz einer SPA zu setzen. Wer will schon auf das Altbekannte, vermeintlich Langweilige setzen und nicht auf das Neueste vom Neuen? Die Entscheidung, eine SPA zu entwickeln, sollte jedoch wohlüberlegt sein, da deren Entwicklung jede Menge zusätzlichen Wissens voraussetzt: Eine weitere Programmiersprache, ein neues Framework, neue Technologien und ein ganz neues Ökosystem. All das, was es auch schon im Backend zu lernen galt, nur eben im Frontend. Dieser Aufwand sollte nicht unterschätzt werden.
Fällt die Wahl nach reiflicher Überlegung doch auf ein Frontend in Form einer SPA, können wir nun auf die eigentliche Frage zurückkommen: Wie beherrscht man die neue Komplexität im Frontend, sodass man am Ende eine funktionierende, aber auch wartbare Anwendung bereitstellen kann – und das am besten in kurzer Zeit?
Eine mögliche Antwort ist: Mehr Backend im Frontend wagen! Das heißt, wir versuchen, bewährte Ansätze und Prinzipien, aber auch Architekturüberlegungen aus dem Backend auf das Frontend anzuwenden. Wann das Sinn ergibt und sogar strikt notwendig ist, und wann eher nicht, zumindest nicht ohne ein größeres Umdenken, werden wir uns im Folgenden anhand einiger Beispiele anschauen.
Zuvor aber noch eine Erklärung wie es zu dieser – hoffentlich hilfreichen – Erkenntnis kam. Einige der besten Frontend-Entwickler, die ich kennenlernen durfte, waren zuvor ausschließlich im Backend tätig. Zum Teil waren sie sogar deutlich besser als Entwickler, die sich bisher nur auf das Frontend konzentrierten, mal abgesehen von visuellen Bereichen wie etwa Styling. Warum traten die vormals im Backend tätigen Entwickler so positiv in Erscheinung? In den meisten Fällen ist die Erklärung einfach: Im Frontend wurde weitestgehend so programmiert, als wäre es das Backend, natürlich unter kluger Einbeziehung der inhärenten Unterschiede zwischen Frontend und Backend.
Damit ein Backend-Entwickler sein volles Potenzial ausschöpfen kann, muss jedoch auch die Einstellung gegenüber dem Frontend stimmen: Wer die Arbeit am Frontend als lästiges Pixelschubsen ansieht, hat die Komplexität moderner SPAs verkannt und wird sich schwertun, das gewohnte Vorgehen aus dem Backend sinnvoll und in ähnlicher Qualität auf das Frontend zu übertragen. Das Frontend ist nicht selten sogar komplexer als das Backend, einfach aus dem Grund, dass eine SPA stark zustandsbehaftet ist. Im Backend hingegen lebt der einzig relevante Zustand oft in der Datenbank oder ist auf die Lebenszeit eines einzelnen Requests beschränkt.
Der wohl erste Schritt, um mehr Backend ins Frontend zu bringen, ist die Wahl der richtigen Programmiersprache. Ein Blick auf die im Backend verwendeten Sprachen lässt schnell erkennen, dass im Enterprise-Umfeld hauptsächlich statisch typisierte Sprachen wie Java – oder neuerdings Kotlin – zum Einsatz kommen. Ideal ist demnach auch eine statisch typisierte Sprache im Frontend. Durch Browser ist JavaScript natürlich gesetzt, aber durch zu JavaScript kompilierende Sprachen wie TypeScript kann man auch im Frontend in den Genuss einer typisierten Sprache kommen (WebAssembly sei hier nur am Rande erwähnt). Mit TypeScript muss man nicht auf den aus dem Backend gewohnten Komfort statisch typisierter Sprachen verzichten. Einige SPA Frameworks, etwa Angular, setzen TypeScript sogar voraus, sodass es kaum Gründe gibt, auf Typisierung zu verzichten.
Auch im Bereich der fachlichen Modellierung, der erfahrungsgemäß im Backend ausgeprägter ist als im Frontend, kann man von vorhandener Erfahrung profitieren; etwa in der Form von Domain-driven Design. Einzig vom oft objektorientierten Ansatz der Modellierung muss man im Frontend leicht abrücken. Im Frontend denkt man im Datenmodell seltener in Klassen, sondern viel häufiger in Objektstrukturen, die durch TypeScript-Interfaces beschrieben werden. Die im Backend üblichen, fachlich getriebenen Methoden weichen daher meist freistehenden Funktionen, die fachliche Operationen auf einem oder mehreren Objekten ausführen. Wie sehr klassengetrieben entwickelt wird, hängt jedoch vom verwendeten Framework ab. Zumindest Services und Komponenten werden in Angular (hauptsächlich aus technischen Gründen) als Klassen abgebildet. Von Vererbung und Co. ist indessen auch hier abzuraten, da diese vor allem im Frontend eher Probleme schaffen als sie zu lösen, auch wenn das in freier Wildbahn leider des Öfteren anzutreffen ist. Komposition sollte auch im Frontend das gewählte Vorgehen sein.
Ein weiterer Aspekt ist die Wahl des verwendeten SPA-Frameworks. Hier lässt sich generell sagen, dass auch im Frontend viele aus dem Backend bekannte Konzepte in das Design heutiger Frameworks eingeflossen sind. Als Hauptvertreter dieser Frameworks sei Angular erwähnt, das bereits viele Funktionalitäten eingebaut hat und sich oft wie bekannte Backend-Frameworks anfühlt. Ein gutes Beispiel stellt die Dependency Injection in Angular dar, die viel mit Spring und CDI gemeinsam hat, sodass sich ein Backend-Entwickler schnell zu Hause fühlt.
Die Vorteile eines am Backend orientierten Frameworks liegen auf der Hand: Als Backend-Entwickler ist man durchaus gewohnt, einen Blick in die ausführliche Spring-Dokumentation oder gar in die Java-EE-Spezifikation zu werfen, um herauszufinden, ob ein Problem bereits klug durch das Framework gelöst wurde, ohne dass man sich selbst die Hände schmutzig machen muss. Idealerweise wendet man dieses Vorgehen auch auf Frameworks wie Angular an und wagt öfter mal einen Blick in die Dokumentation. Das ist auch leider oft notwendig, da Frameworks wie Spring und Java EE, aber eben auch Angular durchaus als zu riesig bezeichnet werden können, als dass man alles im Kopf behalten könnte. Im Frontend erlebt man immer noch zu oft, dass die Möglichkeiten des Frameworks nicht vollständig ausgeschöpft werden, obwohl es sich für das vorliegende Problem anböte.
Natürlich gilt, wie im Backend, stets abzuwägen, wie sehr man sich an das verwendete Framework ketten möchte; bei fachlichen Teilen der Anwendung eher weniger als bei rein technisch getriebenen Problemstellungen. Im Frontend sind die Frameworks noch etwas jünger und weniger ausgereift als im Backend, also sollte man hier noch genauer bewerten, ob man nicht bei der Fachlichkeit auf die Vorteile des Frameworks verzichtet und somit unabhängiger bleibt.
In technischen Bereichen einer SPA können ebenfalls bewährte Backend-Konzepte auf das Frontend gemünzt werden. Ein oft unterschätzter Ansatz, der auch im Frontend Sinn ergibt, ist die State Machine. Im Backend ist eine State Machine oft Mittel der Wahl, um komplexe Zustände, die Übergänge zwischen diesen Zuständen und die dabei ausgeführten Seiteneffekte zu modellieren. Während sich eine State Machine gut verwenden lässt, um fachliche Logik abzubilden, lohnt sich das im Frontend auch für technische Problemstellungen. Gerade komplexe Komponenten werden zu oft durch implizite State Machines umgesetzt, was gerne in vielen scheinbar zusammenhanglosen Variablen und über die Komponente verstreuten Seiteneffekten endet. Eine explizite State Machine kann das Verhalten deutlich verständlicher machen. Die Bibliothek XState sei hier als gängige Basis für State Machines genannt.
Als Backend-Entwickler gehört die Verwendung einer IDE und den darin integrierten Entwicklungstools zum Alltag. Die meisten Entwickler sind geübt im Umgang mit dem Debugger. In der Welt der SPAs ist die Integration in die IDEs leider bisher weniger ausgereift. Stattdessen bringt der Browser entsprechende Tools mit und verhält sich ähnlich zu den aus IDEs bekannten Tools. Ein guter Entwickler sollte den Umgang mit diesen Tools beherrschen, um bei der Fehlersuche schnell zu Ergebnissen zu kommen. Mindestens die Entwicklerkonsole des Browsers sollte jeder Frontend-Entwickler stets geöffnet haben, um nicht vor dem Problem „Irgendwas geht hier nicht“ stehen zu müssen. Wie auch im Backend sollte der erste Reflex sein, den Debugger zu bemühen.
Im Backend bereits gemeistert ist der Umgang mit Abhängigkeiten zu Drittbibliotheken. Während dort fast schon religiös darauf geachtet wird, nur eine Handvoll ausgewählter Bibliotheken – und auch nur jene mit hoher Qualität – zu verwenden, ist die Menge von Abhängigkeiten, die eine übliche SPA verwendet, durchaus beunruhigend. Die Abhängigkeiten sollten weise gewählt und nicht jede noch so kleine Funktionalität von Dritten bezogen werden. Zu beachten ist jedoch, dass npm, der Package-Manager der JavaScript-Welt, grundsätzlich anders funktioniert als etwa Maven in der Java-Welt. Ein Entwickler muss genauso vertraut mit npm wie mit Maven sein, um so klassische Fehler wie das Nichteinchecken des Lockfiles in Git zu vermeiden.
Warum eine SPA oft mehr Abhängigkeiten als das Backend hat, ist schnell erklärt: JavaScript hat keine nennenswerte – und schon gar keine mit Java vergleichbare – Standardbibliothek. Viele Funktionalitäten, die im Backend als gegeben gelten, sind in JavaScript nicht vorhanden und müssen daher von Hand implementiert oder von Dritten bezogen werden. Im Backend ist man sich der Vorzüge einer ausführlichen Standardbibliothek schon lange bewusst, nicht zuletzt, weil sie – einmal verinnerlicht – eine enorme Erhöhung der Produktivität schafft; das gilt natürlich auch für Bibliotheken wie Apache Commons oder Guava, die oft zum Einsatz kommen.
Um den Vorteil einer Standardbibliothek auch im Frontend zu realisieren, sollte man sich auf einen Quasistandard einigen, statt jede einzelne Funktionalität separat durch eine eigene Bibliothek zu beziehen. Neben der stetig anwachsenden Menge an Funktionen, die JavaScript selbst mitbringt, gibt es zum Glück einige bekannte Vertreter wie etwa Ramda oder Lodash, die viele der gewohnten Funktionen aus dem Backend mitbringen.
Es gibt auch vermeintliche Kleinigkeiten, die im Backend übliches Vorgehen darstellen, im Frontend jedoch höchstens stiefmütterlich behandelt werden, wie etwa Logging. Im Backend wird sowohl fachlicher als auch technischer Code mit Logging versehen, um insbesondere bei der Fehlersuche schnell die Ursache zu finden. Gerade im Frontend, wo Zustandsbehaftung und Asynchronität das Debuggen im Gegensatz zum Backend deutlich erschweren, kann Logging eine enorme Hilfe sein, um den Programmfluss besser zu verstehen und so die Ursache eines Fehlers zu finden. Hier reicht es schon, wenn der Fokus auf der Entwicklung liegt und nicht, wie im Backend, auch auf der Speicherung der Logs.
Ein weiteres Thema ist Modularisierung. In fast allen Backends kommt eine Modularisierung mit Hilfe von etwa Maven zum Einsatz. Aus gutem Grund: Module helfen, die Anwendung zu strukturieren und Bereiche voneinander abzugrenzen, sei es entlang der Schichten einer Architektur oder anhand fachlicher Teilbereiche der Anwendung. Nicht zuletzt kann gute Modularisierung über die Zukunft der Anwendung entscheiden, etwa wenn es an das Zerschneiden in Microservices geht. Das Frontend hingegen wird selten als Sammlung zusammengehöriger Module verstanden – von eher technischen Modulen in Angular einmal abgesehen. Dabei lässt sich auch ein Frontend gut modularisieren, etwa in Form von npm-Modulen, die zusammengesteckt eine Anwendung ergeben. Als Lösung bietet sich etwa Lerna, Nx oder Angular Workspaces an. Eine Modularisierung legt, analog zu Microservices im Backend, den Grundstein in Richtung Microfrontends, also die Aufteilung in eigenständige Mini-SPAs, was durchaus sinnvoll bei großen Anwendungen sein kann.
Es gibt jedoch Bereiche, bei denen ein für Backend-Entwickler großes Umdenken nötig ist. Sofern im Backend auf klassische Frameworks wie Spring oder Java EE gesetzt wurde, verfällt man im Frontend schnell in das Denkmuster, alles sei synchron. Während es im Backend völlig legitim ist, in einer von einem HTTP-Controller aufgerufen Methode einen blockierenden Datenbankaufruf oder eine mehre Sekunden dauernde Berechnungen anzustoßen, ist das im Frontend entweder nicht möglich oder sorgt gar dafür, dass die Anwendung aus Benutzersicht eingefroren oder anderweitig nicht mehr benutzbar ist. Um als Backend-Entwickler also im Frontend gute Arbeit zu leisten, ist es unabdingbar, das Ausführungsmodell der JavaScript-Umgebung zu verstehen, insbesondere in Bezug auf Asynchronität; dazu gehört das Verständnis von Events und Promises oder gar Observables.
Bei OPEN KNOWLEDGE hat sich gezeigt, dass bei neuen Projekten die Planung einer Anwendung in Form einer SPA mindestens genauso sorgfältig durchgeführt werden muss, wie das bei eher klassischen Backend-Anwendungen der Fall ist. Dazu gehört neben Technologieentscheidungen, Architekturüberlegungen und dem fachlichen Schnitt der Anwendung natürlich auch die grundsätzliche Betrachtung, ob eine SPA überhaupt die passende Lösung für ein Projekt ist, oder ob eine bewährte Technologie die bessere Wahl wäre. Ein Austausch mit dem Kunden, um die genauen Anforderungen an das Frontend besser zu verstehen, bewirkt oft Wunder, um eine falsche Entscheidung für oder gegen eine SPA zu vermeiden.
Die genannten Beispiele zeigen, dass im Backend gesammeltes Wissen einen enormen Mehrwert für die Entwicklung einer SPA bietet. Die leider noch kleine Zahl guter Frontend-Entwickler kann also gut durch vorhandene Backend-Entwickler kompensiert werden. Voraussetzung ist die richtige Einstellung des Entwicklers und ein wenig Feingefühl, wann der Einsatz von mehr Backend im Frontend sinnvoll ist. Ein nicht zu vernachlässigender Nebeneffekt ist hier auch die Vermeidung von allzu oft noch vorherrschenden Grabenkämpfen zwischen Frontend- und Backend-Entwicklern, die einem ganzheitlichen Produktgedanken im Weg stehen können. In diesem Sinne: Mehr Backend im Frontend wagen!