Was kommt nach der Single-Page-App?

We do it ROCA-Style!

André von Deetzen, Jacob Fahrenkrug

Welche Architektur wählen Sie, wenn Sie den Relaunch einer Ihrer Webanwendungen angehen wollen? Ältere Browser sowie mobile Clients müssen unterstützt werden. Die gesamte Funktionalität muss auch ohne den Einsatz von JavaScript gewährleistet bleiben. Einzelne funktionale Säulen der Anwendung müssen unabhängig voneinander betrieben und entwickelt werden können. Wir haben uns für ROCA-Style entschieden, um eine skalierbare, entkoppelte und den REST-Prinzipien folgende Webanwendung zu entwickeln. ROCA-Style [1] besteht aus einer Sammlung von Grundsätzen, die auf eine hohe Entkopplung von Server und Client abzielen. Unabhängig von Technologien, Werkzeugen und Programmiersprachen kann durch die Befolgung dieser Grundsätze ein Ökosystem von einander ergänzenden Server- und Clientanwendungen geschaffen werden.

Im Juni 2012 haben wir angefangen, eine neue Webanwendung zu entwickeln und mussten uns entscheiden, welchen Architekturansatz wir wählen. Aufgrund der breiten Zielgruppe der E-Post benötigen wir Unterstützung für viele verschiedene Browser in unterschiedlichen Versionen. Dabei soll die gesamte Funktionalität auch ohne Einsatz von JavaScript zur Verfügung stehen. Einzelne funktionale Säulen der Anwendung (Abb. 1) sollen unabhängig voneinander betrieben werden können. Jede Säule bietet Dienste an, die von verschiedenen Clients genutzt werden. Die Clients sind in ihrer Ausprägung sehr unterschiedlich und reichen vom OS-X-Desktop-Client bis zum Customer-Relationship-Management-System. Es werden nicht alle Clients von der hauseigenen IT entwickelt; Partner können eigenständig gegen die APIs ihre Anwendungen entwickeln. Eine weitere Herausforderung ist die Skalierbarkeit auf mehrere Entwicklerteams, die die einzelnen Dienste unabhängig voneinander entwickeln können. Die Dienste werden individuell releast und in der Produktionsumgebung in Betrieb genommen. Eine Webanwendung als Konsument dieser Dienste muss robust gegenüber Schnittstellenerweiterungen sein und tolerant mit Downtimes der anderen fachlichen Säulen umgehen können.

Unsere Alternativen für die Implementierung waren die klassische Webanwendung, basierend auf serverseitigen Sitzungsdaten, und eine Single-Page-App mit clientseitigem Rendering.
Beide Ansätze genügten unseren Anforderungen nicht. Serverseitige Sitzungsdaten erhöhen die betriebliche Komplexität, und eine Single-Page-App ist schwer zu implementieren, wenn ältere Browser unterstützt und die volle Funktionalität ohne JavaScript gewährleistet werden soll. Unsere Wahl fiel deshalb auf ROCA-Style.

Abb. 1: Fachliche Säulen einer Webanwendung

Einführung ROCA-Style

ROCA ist ein Satz von Empfehlungen, die, unabhängig von Webframeworks oder Programmiersprachen, aus Best Practices der Webarchitektur resultieren. Hinter ROCA stehen Till Schulte-Coerne, Stefan Tilkov, Robert Glaser, Phillip Ghadir und Josh Graham, die eine Sammlung von empfohlenen Konzepten auf einer Website [1] mit der Community teilen.

Die ROCA-Guidelines gliedern sich in mehrere server- und clientseitige Empfehlungen. Auf der Serverseite steht die strikte Einhaltung der REST-Prinzipien [2]. Dabei hat jede Ressource einen eindeutigen URI. Auf serverseitigen Zustand wird verzichtet, denn jeder Request soll in sich alle notwendigen Informationen zur Verarbeitung enthalten. Dazu gehören Aspekte wie z. B. Authentifizierung. Ebenfalls sollen die HTTP-Methoden entsprechend ihrer Definition unter [3] eingesetzt werden. Durch den strikten Einsatz kann auch ein URL jederzeit kopiert, weitergegeben, ein Lesezeichen gesetzt oder die Ressource jederzeit eindeutig angefragt werden. Dadurch, dass die gesamte Geschäftslogik auf dem Server gehalten und nicht teilweise in JavaScript ausgelagert wird, kann eine Webanwendung auch mit der Kommandozeile (Wget, cURL) bedient werden. Andere Repräsentationsformate wie z. B. JSON [4] oder XML [5] sind dabei sogar erwünscht. Die Authentifizierung am Server sollte idealerweise per HTTP Basic Authentication über SSL oder Clientzertifikate geschehen. In der Praxis mit Webanwendungen ist dies jedoch umständlich, da Browser keine Standardfunktionalität für eine Abmeldung vorsehen oder ein Styling des Anmeldedialogs zulassen. Daher sind auch formularbasierte Anmeldedialoge in Verbindung mit Cookies erlaubt. ROCA sieht nur zwei Arten der Benutzung von Cookies vor: für die Authentifizierung und das Tracking von Nutzern.

Auf der Clientseite gibt ROCA die Empfehlung, semantisch korrektes HTML auszuliefern. Für das Styling einer Seite soll das Prinzip von Progressive Enhancement [6] verwendet werden. Das Gleiche gilt für JavaScript, um die Webanwendungen auch für ältere Browser nutzbar zu halten. CSS- und JavaScript-Code sollen unter statischen Domains erreichbar sein. Das erleichtert das Caching durch ein Content Delivery Network (CDN) oder das parallele Laden von statischen Ressourcen im Browser. Dadurch können eine schnellere Seitenladezeit und eine angenehmere Wahrnehmung der User Experience erreicht werden. Ebenfalls zur User Experience gehören funktionierende Browser-Controls (Vor, Zurück, Aktualisieren). Da nur eindeutige URIs und die HTTP-Methoden gemäß ihrer Definition eingesetzt werden, kann die Seite jederzeit gefahrlos neu geladen oder mit den Vor– und Zurück-Buttons navigiert werden. Wenn Ressourcen durch einen JavaScript Call geladen werden, kann über das von HTML5 bereitgestellte History-API dieser Grundsatz auch mit JavaScript-Funktionalität abgedeckt werden.

ROCA-Style, eine Implementierung

Im ersten Abschnitt haben wir unsere Beweggründe für eine Anwendung im ROCA-Style dargelegt. In diesem Abschnitt möchten wir näher darauf eingehen, mit welchen Technologien und Architekturgrundsätzen wir den ROCA-Style bei uns konkret implementieren. Dabei haben wir die folgenden drei Schwerpunkte identifiziert.

Zustandslosigkeit

Unsere Webanwendung basiert auf einem Java-Stack. Von uns genutzte serverseitige Frameworks sind Spring MVC und Spring Security. Als View-Technologie kommen JavaServer Pages (JSP) zum Einsatz. Ausgeliefert wird die Anwendung als WAR, um in einem Tomcat [7] betrieben zu werden. Durch den Grundsatz der eindeutigen URIs versetzen wir den Nutzer in die Lage, jederzeit Lesezeichen in unserer Anwendung zu setzen oder z. B. einen Link für einen Kontakt per E-Mail an einen Kollegen zu versenden. Die Authentifizierung überprüft dann, ob der Empfänger berechtigt ist, diesen Kontakt einzusehen. Durch die verschlüsselte Ablage von Authentifizierungsinformationen im Cookie können wir ohne serverseitige Sitzungsdaten auskommen, müssen jedoch jeden Request explizit auf Autorisierung prüfen. Durch den Verzicht auf eine Session unterstützt das System bei Bedarf eine dynamische horizontale Skalierung. Auf diese Weise können Lastspitzen abgefangen werden. Auch die Bereitstellung neuer Versionen unter Einsatz eines BlueGreenDeployments [8] ist so möglich.

Statisches CSS und Unobtrusive JavaScript

Ein weiterer Grundsatz von ROCA ist, dass CSS und JavaScript als statische Ressourcen ausgeliefert werden. Mobile Clients sowie Clients mit einer langsamen Internetverbindung können davon durch effizientes Caching profitieren. Obwohl davon ausgegangen werden kann, dass die meisten Browser CSS und JavaScript unterstützen, sind die Unterschiede in den Implementierungen groß. Auch ist es möglich, dass der Nutzer JavaScript deaktiviert hat. ROCA bietet uns mit „Unobtrusive JavaScript“ die Möglichkeit, unsere Anwendung je nach Fähigkeiten der Browser ergonomischer darzustellen. Konkret bedeutet das, dass wir, sobald JavaScript aktiviert ist, mit Ajax-Calls und DOM-Manipulation arbeiten. Dadurch können bspw. Klickpfade verkürzt und mehrere Requests im Hintergrund parallel abgearbeitet werden, um dem Nutzer eine ansprechende und ergonomische Oberfläche zur Verfügung zu stellen. Für den Fall, dass JavaScript deaktiviert ist, wird dem Benutzer die sonst mit JavaScript implementierte Funktionalität in Form von Links angezeigt. Folgt der Nutzer dem Link, gelangt er zu einer separaten Seite, die die gewünschten Informationen und Funktionalitäten zur Verfügung stellt. Wir konnten unser Ziel erreichen, dem Nutzer trotz deaktivierter JavaScripts den vollständigen Funktionsumfang mit Einbußen in der User Experience zur Verfügung zu stellen. Man kann den Einsatz von Unobtrusive JavaScript folgendermaßen umsetzen:

<input type=“text“ name=“email“ id=“email“ />

Das HTML repräsentiert ein Eingabefeld für eine E-Mail-Adresse. Durch die Vergabe der id email kann in dem folgenden JavaScript einfach die onchange-Methode ergänzt werden:

window.onload = function() {
    document.getElementById('email').onchange = verifyEmailAdress;
};

Der klassische Einsatz von JavaScript sieht im Vergleich dazu so aus:

<input type=“text“ name=“email“ onchange=“verifyEmailAdress();“ />

Komposition von Systemen

Die Webanwendung konsumiert verschiedene andere Dienste, um unseren Kunden ein durchgängiges Nutzererlebnis in unserem Produkt zu gewährleisten. Um einen hohen Konfigurationsaufwand zu vermeiden, verwenden wir für alle unsere Dienste einen Hypermediaansatz. Wenn unsere Webanwendung eine andere fachliche Säule integriert und die Links zu diesem System generieren möchte, wird als Erstes das Servicedokument des Diensts angefragt, das alle weiteren Informationen zur Nutzung enthält. Das macht es besonders leicht, die Anfragen auf neu bereitgestellte Systeme umzulenken, ohne dass der Nutzer abgemeldet werden muss oder gar eine Wartungsseite zu sehen bekommt. Die Servicedokumente sind alle unter einem statischen URI erreichbar und exponieren alle Funktionalitäten zur Nutzung des Diensts. Dabei folgen diese dem HATOES-Prinzip (siehe Kasten), und der Nutzer kann entweder durch JavaScript oder durch das Verfolgen von Links durch die Anwendungen navigieren. Zurzeit verbindet die Webanwendung ein Mailbox-API, das auch von unseren mobilen Clients benutzt wird, und ein Send-API, das von externen Partnern benutzt wird. Wir planen, weitere APIs zu entwickeln, wie beispielsweise ein Adressbuch oder einen Faxversand. Diese Funktionalitäten wollen wir dann auch für unsere Nutzer in der Webanwendung zur Verfügung stellen. Eine Webanwendung aus verschiedenen Diensten zu komponieren, die über Links miteinander lose gekoppelt sind, bietet eine Reihe von Vorteilen. Die Nutzung von Links führt zu einem stabilen und fehlertoleranten System. Denn wenn ein Dienst vorübergehend nicht erreichbar ist, führt der Link vielleicht ins Leere. Alle anderen Funktionen können jedoch trotzdem weiter genutzt werden. Ebenfalls wird der Nutzer nicht ausgeloggt, sondern kann einfach über den Back-Button seines Browsers in den Ursprungszustand zurückgelangen. Die angezeigten Ressourcen sind genauso Cache-bar wie die statischen Ressourcen (CSS, JavaScript). Durch den Einsatz von Cache-Headern haben wir ein sehr performantes System geschaffen.

HATEOAS
HATEOAS steht für „Hypermedia as the Engine of Application State“. Es handelt sich um eine Einschränkung, die die REST-Architektur von den meisten anderen Netzwerkarchitekturen unterscheidet. Dem HATEOAS-Prinzip folgend, interagieren Clients mit einer Webanwendung ausschließlich über Hyperlinks, die dynamisch von der Webanwendung zur Verfügung gestellt werden. Ein REST-Client braucht keinerlei Wissen über die Implementierung des Service. Alles, was notwendig ist, ist ein generelles Verständnis von Hypermedia [9].

Ausblick

Mittelfristig wollen wir unser gesamtes System in kleine Dienste zerlegen. Dabei ist jeder Dienst einzeln betreibbar und zustandslos. Alle Dienste werden von uns selbst betrieben und durch eine Continous-Delivery-Pipeline, wenn nötig mehrfach täglich, in neuen Versionen in Betrieb genommen. Eine Integration mit Diensten, die wir nicht betreiben, ist zwar möglich, von uns jedoch im Moment nicht gewollt. Dieses Vorgehen erlaubt uns sehr viel Freiheit und Flexibilität, um schnell auf Kundenwünsche sowie den Markt zu reagieren.

Durch die Nutzung von HTTP und REST ist die vorstellbare Anzahl an Clientimplementierungen beliebig. Jeder Dienst liefert, je nach angefragtem „Application Type“, JSON, HTML oder XML und kann in unsere Webanwendung integriert werden. Unsere REST-APIs sind stabil und werden abwärtskompatibel weiterentwickelt, um bestehende Clientimplementierung nicht zu stören. Für die Absicherung unserer Systeme setzen wir OAuth 2.0 [10] ein. So können wir sicherstellen, dass zu jedem Zeitpunkt überprüft werden kann, ob die Aktion in unserem System ausgeführt wird und die Person oder das Clientsystem dazu berechtigt ist. Die Ablage der Authentifizierungsinformationen erfordert keine mehrfache Anmeldung zwischen den Systemen. Ein einheitliches Design stellt dabei das Nutzererlebnis sicher, während sich unsere Nutzer nahtlos über Systemgrenzen hinweg bewegen, ohne davon Kenntnis zu nehmen.

Fazit

Eine Kernkompetenz erfolgreicher Internetunternehmen ist die Erprobung von Ideen und das Eliminieren von Fehlversuchen. Wir sind der Überzeugung, mit ROCA die richtige Designentscheidung getroffen zu haben, um einem modernen Internetunternehmen und seinen Anforderungen gerecht zu werden. Durch ROCA haben wir eine lose Kopplung zwischen unseren Anwendungen geschaffen und können das System bei Bedarf erweitern oder verkleinern. Die aus unserer Sicht wichtigsten Merkmale sind sowohl die Zustandslosigkeit auf der Serverseite und der Einsatz von „Unobtrusive JavaScript“ auf dem Client, als auch die Umsetzung von REST-Prinzipien für unsere Anwendung. Viele Vorteile des Webs können wir nutzen, um unsere Webanwendung stabil, fehlertolerant, performant und für ein breites Spektrum von Nutzern und Clientimplementierungen zur Verfügung zu stellen. Eine solche Anwendung zu entwerfen bedeutet aber auch einen nicht zu unterschätzenden höheren Aufwand an Arbeit und Disziplin bei der Implementierung.

Geschrieben von
André von Deetzen
André von Deetzen
  André von Deetzen (andre.von.deetzen@epost-dev.de) ist Principal Engineer bei der Deutsche Post E-POST Development GmbH. Seine Schwerpunkte sind die agile Softwarentwicklung, Architektur und die Delivery-Pipeline in die Produktion, um Produkte schnell und in guter Qualität ausliefern zu können.
Jacob Fahrenkrug
Jacob Fahrenkrug
  Jacob Fahrenkrug (jacob.fahrenkrug@epost-dev.de) ist Seniorarchitekt bei der Deutsche Post E-Post Development GmbH. Als Teil eines Scrum-Teams treibt er die Entwicklung des E-Post-Ökosystems und dessen Architektur voran.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.