Suche
Kolumne

EnterpriseTales: Warum Offline-First-Architekturen die Zukunft gehört

Lars Röwekamp

Webanwendungen sind auch nicht mehr das, was sie mal waren. Während früher ein Wegfall der Netzwerk-Connectivity automatisch auch zu einem Ausfall der aufgerufenen Seite führte, werden heute, im Falle eines Falles, die darzustellenden Inhalte aus den Tiefen des Browsers hervorgezaubert und gaukeln so dem Nutzer eine permanente Verfügbarkeit vor. Offline First, so das Zauberwort der Stunde.

Die Zeiten, in denen die Requests des Browsers direkt an den Server gingen und von dort synchron passende Responses zurückgeliefert haben, scheinen gezählt. Mehr und mehr macht sich auch im Webumfeld das asynchrone Kommunikationsmuster breit. Immer mit dem Ziel einer „non blocking“ UI und einer damit einhergehenden bestmöglichen User Experience. Inhalte werden im Browser reaktiv aktualisiert, sobald sie vom Server geliefert werden. Kann dieser nicht liefern, werden sie aus dem Cache gezogen. Was so einfach klingt, ist ein völlig neues Paradigma in der Webentwicklung. Das Ganze ist Fluch und Segen zugleich. Denn dem Vorteil einer scheinbar permanent zur Verfügung stehenden Webseite steht eine ganze Menge neuer Herausforderungen gegenüber. Gut gemacht, ergibt sich für den Benutzer eine stark verbesserte Usability gegenüber dem klassischen Webmodell. Schlecht gemacht, siegt dagegen am Ende der Frust. Ach ja, und dann ist da ja auch noch das kleine Problem der Browserkompatibilität. Aber dazu später mehr.

Die Frage nach dem Warum

„Offline First? So ein Kokolores! Das haben wir früher doch auch nicht gebraucht.“, würde mein Großvater sagen. Die Skepsis ist durchaus berechtigt. Warum sollten wir heute Zeit und Geld investieren, um eine Webanwendung offlinefähig zu machen, wenn es bisher doch auch ganz gut ohne ging? Nur weil es technologisch möglich ist? Hier könnte man ketzerisch die Frage anschließen, warum es heute technologisch möglich ist. Ohne Nachfrage kein Angebot. Oder anders formuliert: Es scheint durchaus einen Bedarf für die Offlinefähigkeit von Webanwendungen zu geben.

Aber warum ist das so? Während wir noch vor einigen Jahren unsere Webanwendungen fast ausschließlich in den eigenen vier Wänden oder am Arbeitsplatz genutzt haben, leben wir heute in einer Ära des „Mobile Mindshift“. Wir erwarten, dass wir zu jeder Zeit an jedem Ort beliebige Dinge und Aufgaben via (Web) App erledigen können. Etwas blauäugig könnte man annehmen, dass das im Jahr 2017 und einer nahezu flächendeckenden Netzabdeckung kein Problem darstellen sollte. Dabei gibt es auch heute noch mehr als genug Situationen, in denen wir offline sind oder die Netzabdeckung nicht ausreichend erscheint. Egal ob beim täglichen Pendeln mit Bus und Bahn, auf einem ausverkauften Festival, in weitläufigen Waldgebieten oder aber in Ländern mit unerschwinglichen Roaminggebühren: Offline ist auch 2017 nach wie vor allgegenwärtig! Wer also die Erwartung seiner Nutzer nicht enttäuschen möchte, der muss über kurz oder lang die Usability seiner Webanwendung auch für den Fall fehlender Netzwerkverbindung optimieren. Die Frage ist nur wie. Eine Frage, die sich sowohl aus ergonomischer als auch technologischer Sichtweise beantworten lässt.

Die Frage nach dem Wie

Würde man tausend Nutzer von interaktiven Webseiten nach dem größten Frustpotenzial und damit einhergehend nach ihren Verbesserungswünschen bezüglich der Ergonomie befragen, dann würden die Antworten mit Sicherheit in etwa wie folgt aussehen:

  • „Ich erwarte ununterbrochenen Zugang zu meinem wichtigsten Content.“
  • „Was ich einmal angefangen habe, möchte ich auch beenden können.“
  • „Content ist änderbar. Daran sollte auch der Online-/Offlinestatus nichts ändern.“
  • „Ich möchte keine Fehlermeldungen bekommen, sondern sinnvolle Hinweise.“
  • „Ich möchte mir meine letzte Aktion nicht merken müssen. Das soll die Web-App für mich tun.“

Es gilt also, eine Situation zu schaffen, in der der Nutzer einer Webseite diese auch dann sinnvoll nutzen kann, wenn einmal – temporär – keine oder nur eine unzureichende Netzwerkverbindung zur Verfügung steht. Der Offlinestatus darf dabei kein Fehler sein, sondern lediglich einer von mehreren gültigen Zuständen. Nimmt man den Begriff Offline First wörtlich, dann ist der Offlinestatus sogar die Basis der Anwendung und der Onlinestatus lediglich ein nettes, wenn sicherlich auch essenzielles Enhancement. Was sich für den einen oder anderen Leser evtl. erst einmal nach utopischen Anforderungen anhört, kennen wir bereits alle aus unserem täglichen IT-Leben: aus Mailprogrammen.

Egal ob online oder offline, Mails lassen sich schreiben und zum Versenden ablegen. Der eigentliche Versand erfolgt, sobald das Mailprogramm online ist. Parallel dazu werden die auf dem Server vorhandenen ungelesenen Mails asynchron im Hintergrund abgerufen. Und es kommt noch besser: Das Ganze funktioniert auch bidirektional über mehrere Clients hinweg, zumindest in den meisten Fällen. Dieses Paradigma gilt es ins Web zu übertragen. Ein populäres Beispiel für eine gelungene Umsetzung ist die Anwendung Google Docs. Egal ob online oder offline, Dokumente können erstellt und verändert werden. Die Anwendung selbst signalisiert dabei dem Nutzer, ob die Änderungen direkt auf dem Server gespeichert wurden oder zunächst einmal nur lokal, um dann später mit dem Server synchronisiert zu werden, sobald die Verbindung wiederhergestellt ist. Natürlich gibt es aber auch weniger bekannte Webseiten, denen es gelungen ist, auch oder gerade im Offlinefall eine gute User Experience zu bieten. Eine Übersicht findet sich hier.

Das eben geschilderte Offline-First-Szenario stellt uns vor zwei unterschiedliche Herausforderungen. Zum einen gilt es, auch dann sinnvolle Daten und Web-Assets anzuzeigen, wenn keine Verbindung zum Server besteht. Zum anderen muss nach dem Wiederherstellen der Verbindung ein Austausch der geänderten bzw. neuen Daten und Web-Assets zwischen Browser und Server – vice versa – erfolgen. Aus technologischer Sicht haben wir es also mit den Problemfeldern Caching und Synchronisation zu tun.

Cache is King

Einmal abgesehen von dem allerersten Zugriff auf eine bis dato noch unbekannte Webseite, könnten im Offlinefall die darzustellenden Inhalte anstelle vom Server auch direkt aus einem Cache geliefert werden. Dies gilt sowohl für die statischen Web-Assets, wie HTML-Seiten, Images, JavaScript- und CSS-Dateien, als auch für dynamische Daten, wie Tweets, Posts, Nutzer-Profiles, Preferences etc. Natürlich wären die dynamischen Inhalte dann eventuell nicht mehr aktuell, aber für viele Anwendungsfälle ist es sicherlich sinnvoller, leicht veraltete Daten anzuzeigen, als gar keine.

Schaut man sich einmal ein wenig in der W3C-Spezifikationswelt um, dann bieten die aktuellen Browser gleich eine ganze Reihe an Optionen zum Caching von statischen und dynamischen Inhalten an. Allen voran der eingebaute Browsercache in Kombination mit entsprechenden HTTP-Headern, die das konkrete Cacheverhalten steuern. Leider zeigt die Praxis, dass die intensive Nutzung des Browsercaches mit einer Menge von Problemen behaftet und dieser daher nicht wirklich für die zuverlässige Verwendung im Rahmen einer Offline-First-Architektur geeignet ist. Das Netz ist voll mit entsprechenden Blogbeiträgen zu diesem Thema, sodass ich an dieser Stelle auf eine weitere Ausführung verzichte. Eine alternative zum Browsercache ist HTML5 Application Cache API (kurz: AppCache). Das gilt zumindest für die statischen Webressourcen. Mithilfe einer Cachemanifestdatei lässt sich deklarativ angeben, welche Webressourcen im Cache gespeichert werden sollen, ob fehlende Ressourcen bei Bedarf aus dem Internet geladen werden dürfen oder ob stattdessen alternativ ein Fallback verwendet werden soll. Klingt eigentlich optimal. Leider zeigen sich aber auch bei der Verwendung des AppCaches eine Reihe von Problemen, sodass es bei nicht hundertprozentig durchdachter Anwendung zu unerwünschten Seiteneffekten kommt. So werden z. B. die Ressourcen auch dann aus dem Cache gezogen, wenn eine Verbindung zum Server besteht. Neuere Versionen auf Seiten des Servers werden ignoriert. Möchte man dieses Verhalten korrigieren, muss die Manifestdatei auf Seiten des Servers geändert werden, was wiederum impliziert, dass man diese niemals cachen sollte. Ebenfalls seltsam ist das Verhalten, wenn innerhalb einer gecachten Ressource auf eine nicht gecachte Ressource zugegriffen wird. In diesem Fall wird auch bei bestehender Netzverbindung nur die gecachte Ressource angezeigt. Erst ein entsprechender Eintrag in der Manifestdatei im NETWORK-Abschnitt sorgt dafür, dass auch die nicht gecachte Ressource angezogen wird. Da der AppCache ein zusätzlicher Cache ist, also den Browsercache nicht ersetzt, sondern lediglich ergänzt, können sich auch durch die Kombination von Manifestdatei und HTTP-Header unerwartete Szenarien ergeben, sodass auch hier Vorsicht geboten ist. Erschwerend kommt hinzu, dass erste Browseranbieter den AppCache als veraltet ansehen und entsprechend als deprecated markiert haben. Eine gute Übersicht über die eben skizzierten Probleme und etliche mehr findet sich in dem Blog „A List Apart“.

Was aber nehmen, wenn der Browsercache und der AppCache nicht in Frage kommen? Gibt es noch weitere Alternativen? Die Antwort ist, wie so oft im Webumfeld, eindeutig mit JEIN zu beantworten. Denn mit dem Service Worker gibt es eine echte Alternative, einen individuellen Caching-Mechanismus aufzubauen, unter Zuhilfenahme des Service-Worker-Cache-API. Nur leider wird diese Alternative (noch) nicht von allen Browsern vollständig unterstützt. Die Idee der Service Workers ist denkbar einfach. Ein einmal für einen URL Scope installiertes Service-Worker-Skript kann, unabhängig von der eigentlichen Webseite, beliebige Aufgaben innerhalb des URL Scopes asynchron im Hintergrund ausführen. Unter anderen kann es ausgehende Requests abfangen und für diese beliebige Responses erzeugen. Genau dieses Feature kann zum Aufbau einer eigenen Cachestrategie verwendet werden. Setzt der URL Scope einen Request an einen Server ab, klingt sich der zugehörige Service Worker dazwischen und prüft, ob er die angefragte Webressource besser aus dem Cache bedienen kann. Ist das der Fall, wird ein entsprechender Response aus dem Cache heraus erzeugt. Alternativ erfolgt ein Zugriff auf den Server oder – im Falle einer fehlenden Verbindung – das Abspielen eines selbstdefinierten Fallback-Szenarios. Da der Ablauf mehr oder minder völlig in der Hand des Entwicklers liegt, sind den möglichen Cachestrategien kaum Grenzen gesetzt. So kann es durchaus sinnvoll sein, die Webseite zunächst aus dem Cache zu bedienen und parallel dazu auf dem Server nach neuerem Content zu schauen. Eine sehr gute Übersicht über verschiedene Service-Worker-Patterns im Allgemeinen und Service-Worker-Caching-Patterns im Speziellen findet sich hier sowie hier. Ob der eigene Browser bereits Service Workers unterstützt und wenn ja, in welchem Umfang, findet sich unter folgendem Link.

Ähnlich verworren wie die Situation bei den Caches für statische Assets sieht es auch bei den Pendants für dynamischen Content aus. Die Spezifikation des einstigen Primus Web SQL Database ist mittlerweile vom W3C eingestellt worden. Als Alternative bietet sich der Local Storage an. Hierbei handelt es sich um einen einfachen Key-Value Store, der auch im Offlinemodus zur Verfügung steht. Das API des Local Storage ist denkbar einfach. Neben den Möglichkeiten zum Anlegen, Auslesen und Löschen von einzelnen Datensätzen (set/get/remove), lässt sich die Länge des aktuellen Caches ermitteln und der gesamte Cache löschen (clear/length). So einfach das API in der Bedienung ist, so eingeschränkt ist auch sein Nutzen. Da sich nur Strings im Local Storage ablegen lassen, müssen Objekte, wie JSON-Objekte, zunächst in Strings überführt werden, bevor sie in den Cache gelangen können. Beim Auslesen ist dann der umgekehrte Weg notwendig. Erschwerend kommt hinzu, dass das API synchron arbeitet, was der Performanz nicht gerade zuträglich ist. Aufgrund einer fehlenden Ablagestruktur, wie wir sie von SQL-Datenbanken her kennen, ist weder das Anlegen eines Indexes noch das gezielte Absetzen von Queries auf den Local Storage möglich. Es gibt zwar den einen oder anderen Aufsatz, der genau dieses Verhalten simuliert. Das macht den Storage-Mechanismus aber nicht gerade schneller. Deutlich komfortabler dagegen kommt die HTML5-NoSQL-Datenbank IndexedDB daher. Die Browserdatenbank ist speziell für die Ablage großer Datenmengen im Browser konzipiert worden. Mit ihrer Hilfe können beliebige Objekte in sogenannten Object Stores – ähnlich den klassischen Datenbanktabellen – als Key-Object-Paare abgelegt werden. IndexedDB erlaubt sowohl Abfragen auf den Daten als auch das Anlegen eines Index zur Performanzoptimierung. Auch Transaktionen und Cursor stehen zur Verfügung. Im Vergleich zum Local Storage ist das API der IndexedDB relativ komplex und damit einigen Entwicklern zu komplex. Als sinnvoller Kompromiss für einfache Szenarien bietet sich die Verwendung des JavaScript-Aufsatzes localForage an. Mithilfe des localForage-API wird von der Komplexität der IndexedDB abstrahiert, ohne dabei auf deren Features verzichten zu müssen.

PouchDB nimmt die Herausforderung an

Bleibt noch der offene Punkt der Synchronisation. Dadurch, dass wir in unserer Offline-First-Anwendung auch dann die Modifikation von lokalem Content erlauben wollen, wenn wir offline sind, müssen wir uns unweigerlich mit der Problemstellung der Browser-Server-Synchronisation auseinandersetzen. Theoretisch kann man den notwendigen Code als JavaScript-Eigenbau mit ausliefern. Wer allerdings jemals einen bidirektionalen Synchronisatiosmechnismus gebaut hat, wird gerne Abstand davon nehmen und auf eine fertige Lösung zurückgreifen. Die wohl bekannteste Lösung ist derzeit die Open-Source-JavaScript-Datenbank PouchDB, die – der Name legt es nahe – stark an CouchDB angelehnt ist. PouchDB ist speziell für die Herausforderung von Offline-First-Applikationen konzipiert und nutzt unter der Haube die eben vorgestellte IndexedDB. Durch Angabe von Change Handlers und Onlinereplikaten lässt sich mithilfe von PouchDB die notwendige Synchronisation mit wenigen Zeilen Code realisieren. Vorausgesetzt auf der Gegenseite am Server existiert eine Datenquelle, die das Couch Replication Protocol versteht oder über einen der zahlreichen Adapter angebunden werden kann.

Fazit

Offline-First Anwendungen gehört die Zukunft, keine Frage. Technologisch bieten HTML5 und Co. bereits heute alles, was es benötigt, um (s)eine Web-App auch im Falle fehlender oder mangelhafter Network Connectivity sinnvoll weiterarbeiten zu lassen. Für statische Assets, wie HTML-Seiten, JavaScript-Dateien oder Images, können Service Workers und eigene Caching-Strategie je nach Offline-/Onlinestatus aus der für den jeweiligen Kontext bestmöglichen Quelle herangezogen werden. Werden Service Workers vom Browser nicht unterstützt – wie im Fall von Safari – kann der Application-Cache als Fallback verwendet werden. Für das Caching von dynamischem Content verwendet man am besten die NoSQL-Browserdatenbank IndexedDB. Wem das API zu komplex ist, kann durch den JavaScript-Aufsatz localForage eine deutliche Vereinfachung erreichen. Spannend wird es bei Offline-First-Anwendungen immer dann, wenn der Connectivity-Modus wechselt. Geht die Anwendung wieder online, muss eine Synchronisation mit dem Server erfolgen. Hier bietet sich die Verwendung eines entsprechenden Frameworks wie PouchDB an. Aber auch der umgekehrte Fall – der Wechsel von online zu offline – bringt einige Herausforderungen mit sich. Insbesondere gilt es, dem User auch weiterhin sinnvolle Aktionen anzubieten und ihn dabei möglichst transparent darüber zu informieren, wie seine Änderungen des Contents offline behandelt werden. Dies ist weniger ein technologisches Problem als eine Frage nach der passenden UX. Am Ende geht es darum, die Erwartungen der Nutzer zu erfüllen. Diese haben sich in den letzten Jahren, dank mobiler Endgeräte und damit verbundener wachsender Mobilität stark verändert. Grund genug, dass auch wir unsere Anwendungen in Richtung Offline First verändern. In diesem Sinne: Stay tuned!

Geschrieben von
Lars Röwekamp
Lars Röwekamp
Lars Röwekamp ist Gründer des IT-Beratungs- und Entwicklungsunternehmens open knowledge GmbH, beschäftigt sich im Rahmen seiner Tätigkeit als „CIO New Technologies“ mit der eingehenden Analyse und Bewertung neuer Software- und Technologietrends. Ein besonderer Schwerpunkt seiner Arbeit liegt derzeit in den Bereichen Enterprise und Mobile Computing, wobei neben Design- und Architekturfragen insbesondere die Real-Life-Aspekte im Fokus seiner Betrachtung stehen. Lars Röwekamp, Autor mehrerer Fachartikel und -bücher, beschäftigt sich seit der Geburtsstunde von Java mit dieser Programmiersprache, wobei er einen Großteil seiner praktischen Erfahrungen im Rahmen großer internationaler Projekte sammeln konnte.
Kommentare
  1. Sezgin2017-08-21 09:15:44

    Fehler bei einem link. ich glaube die korrekte webseite ist: http://offlinestat.es/ (NICHT: http://offlinstat.es/) da wurde das 'e' vergessen

  2. Sezgin2017-08-21 09:21:06

    PouchDB Link ist teilweise defekt. bitte http://pouchdb.com/ (ohne 's' beim http) aufrufen. sie werden automatisch ins 'https' weiter geleitet.

  3. Hartmut Schlosser2017-08-21 10:20:52

    Vielen Dank für die Hinweise! Wir haben die Links korrigiert.

Schreibe einen Kommentar

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