Der Aufbau einer Offline-First-Anwendung

JavaScript-Anwendungen haben in den letzten Jahren stark an Beliebtheit gewonnen, insbesondere durch gut etablierte Frameworks wie vue.js, React und Angular sowie die Möglichkeit, mit TypeScript umfangreiche Geschäftsanwendungen zu entwickeln. Der Einsatz von Browser-Engines ermöglicht es diesen Anwendungen, geräteunabhängig zu sein. Sie benötigen lediglich einen Browser, um ausgeführt zu werden und können auch offline sein.

In diesem Artikel beleuchte ich die Architektur von JavaScript-basierten Rich Clients, sowie die Bedeutung der Benutzeroberfläche für geschäftskritische Anwendungen und beschreibe die Fortschritte in der Webanwendungsentwicklung, insbesondere den Übergang zu einem ereignisgesteuerten Ansatz und einem Offline-First-Ansatz.

Kurz & knapp

  • JavaScript-basierte Frameworks für den Browser werden für die Erstellung von Single-Page-Webanwendungen für Unternehmen verwendet.
  • Neue JavaScript-Konzepte wie Zustandsverwaltung und lokale Speicherung im Browser, führen zu einem anderen Design von REST-Schnittstellen.
  • Die Offline-Fähigkeit ist mehr als die Offline-Speicherung von Daten. Sie erfordert weitere Architekturkonzepte.

Die Kombination von JavaScript, das in einem Browser läuft und daher plattformunabhängig ist, TypeScript und ausgereiften JS-Frameworks führt zu größeren geschäftskritischen Anwendungen, die sehr ähnlich sind wie “Rich Clients” basierend auf Java Swing oder 4GL-Programmiersprachen:

  • Authentifizierung und Autorisierung geschützt
  • Client-Server-Kommunikation
  • “Intelligente Clients”
  • Fähigkeit zur Offline-Arbeit
  • Für Unternehmensanwendungen: Verwendung mehrerer Masken und mehrerer Module/Unteranwendungen

Neben der zugrunde liegenden Technologie besteht ein wesentlicher Unterschied in der Interaktion mit dem Server. Die Datenbankverbindung der alten Rich Clients wird durch die zustandslose REST-Kommunikation ersetzt.

JavaScript-basierte Rich-Client-Architektur

Bei geschäftskritischen Anwendungen nutzen Benutzer diese intensiv. Die Benutzeroberfläche muss sowohl hinsichtlich des Arbeitsablaufs als auch der Anzeige der richtigen Informationen optimiert sein. Die Reaktionsgeschwindigkeit ist sehr wichtig. Wenn Sie durch die Anwendung navigieren und viele Menschen mit der Anwendung arbeiten, möchten Sie nicht mehrere Sekunden warten, bis der nächste Bildschirm angezeigt wird. Die nächste Maske sollte sofort erscheinen. Dies wird erreicht, indem der Zustand im Client beibehalten und nur Änderungen in Form von JSON-Dokumenten an den Server übertragen werden.

Die Architektur der Webanwendungen hat sich in den letzten Jahren auch stark verändert. Abkehr vom serverseitigen Rendern und späterer Verwendung von AJAX-Anfragen ersetzt serverseitiges Rendern und sendet nur Daten an die Webseite. Der JavaScript-Code rendert dann den Inhalt. Aus Sicht der Benutzererfahrung ist dies eine Verbesserung, da nur die Daten an den Client gesendet werden müssen. Dies ist schneller als das Rendern der gesamten Webseite. Mit den neuen Konzepten der Zustandsverwaltung und des lokalen Speichers in der Welt von JavaScript können Daten lokal im Browser gespeichert werden.

Dies ermöglicht ein anderes Design der REST-Schnittstellen und der Datenverarbeitung. Es führt zu einer anderen Art von Client-Anwendung. Eine Progressive Web App (PWA) definiert beispielsweise einen Satz von Technologien, die verwendet werden, um nicht nur eine Webanwendung im Browser auszuführen, sondern eine vollständige Anwendung, die installiert und als eigenständige Anwendung ausgeführt werden kann. Eine Progressive Web App ist eine App, die Webplattformtechnologien verwendet, aber eine Benutzererfahrung wie eine plattformspezifische App bietet.

Offline-Anwendungen

Ein Bestandteil einer PWA-Anwendung ist ihre Offline-Fähigkeit. Das zugrunde liegende Architekturmuster besteht darin, Daten immer lokal zu speichern und die Kommunikation asynchron im Hintergrund durchzuführen. Die Anwendung verwendet hauptsächlich die lokal verfügbaren Daten. Die Nutzung der Anwendung ist unabhängig davon, ob eine Netzwerkverbindung verfügbar ist oder nicht, oder ob das Netzwerk eingeschränkt ist. Dies ermöglicht nicht nur die Verwendung der Anwendung im Offline-Modus, sondern macht die Anwendung auch wesentlich schneller. Da die Daten lokal gespeichert sind und daher verfügbar, erfolgt die Antwort auf eine Benutzerinteraktion direkt mit den lokal verfügbaren Daten. Die lokalen Daten werden im Speicher (z. B. Redux Store) und in der Index-Datenbank für langfristige Persistenz gespeichert. Da es Daten auf dem Client und Daten auf dem Server gibt, ist ein Synchronisationsmechanismus erforderlich. Das bedeutet, dass Änderungen auf dem Client an den Server gesendet werden und der Client neue Änderungen vom Server erhält. Normalerweise wird eine generische Logik verwendet, die sich um die Daten­synchronisation unabhängig vom Datentyp kümmern kann. Wenn die Datensynchronisation auf JSON-Objekten basiert, werden nur die neuesten Änderungen gesendet. Ältere Versionen der geänderten Objekte werden nicht mehr benötigt und verworfen.

Je nach Daten und Handhabung der Offline-Situation gibt es verschiedene Möglichkeiten, Daten mit einem Backend-System zu synchronisieren: Wenn Sie vollständige JSON-Objekte haben, können alle geänderten Objekte an den Backend-Server gesendet werden. Eine Alternative besteht darin, nur die Delta-Informationen (z. B. geänderte Felder) zu senden oder einen event-sourcing Ansatz zu verwenden.

Auf einer abstrakten Ebene klingt es einfach, Daten offline verfügbar zu haben und alle Datensynchronisationen im Backend durchzuführen – aber das ist es nicht. Wenn Daten offline gehalten und mit einem Backend-Server synchronisiert werden und die Daten auf regulären Geschäftseinheiten basieren, die von mehreren Benutzern verwendet werden, können Konflikte auftreten, die von der Anwendung oder der Synchronisations­schicht behandelt werden müssen.

Da Daten auf dem Client asynchron verarbeitet werden, kann ein “Warteschlangenmechanismus” verwendet werden, um die Datenverarbeitung zu steuern. Zum Beispiel können eine eingehende und ausgehende Warteschlange das Senden und Empfangen von Daten von den gespeicherten Daten entkoppeln. Es entkoppelt auch die Persistenztechnologie von der Synchronisationslogik und ermöglicht eine spezifische Implementierung für die Persistenz oder verschlüsselte Daten. Die Verwendung eines Redux Stores in einer React-basierten Anwendung ermöglicht es auch den UI-Komponenten, auf Änderungen im Redux Store zu reagieren.

Vorteile von Offline-Fähigkeiten

Nachdem ein Mechanismus für Daten-Synchronisierung eingerichtet ist, ermöglicht er neben der Offline-Fähigkeit drei Szenarien, wie in der folgenden Abbildung dargestellt:

  1. Die Benutzeroberfläche wird aktualisiert, wenn neue Daten vom Backend empfangen werden.
  2. Die Synchronisation erfolgt nicht nur zwischen einem Client und einem Server, sondern auch zwischen zwei Clients (über einen Server).
  3. Eine Event-basierte Architektur durch das Verwenden des Command-Patterns in JSON-Objekten über die synchronisierten Datenobjekte

Die oben beschriebene Daten-Synchronisierung, die die Offline-Nutzung ermöglicht, eignet sich nicht für alle Arten von Anwendungen. Es gibt Anwendungen, bei denen Änderungen sofort wirksam sein müssen, wie zum Beispiel bei der Benutzerverwaltung oder wenn Sie eine Steuerungs-Benutzeroberfläche für einen Schalter haben. Auch wenn Sie eine Anwendung haben, bei der Sie viele Daten benötigen, beispielsweise für Berichterstattungszwecke, ist der Aufwand, die Daten offline verfügbar zu halten und zu synchronisieren, deutlich höher.

Eine Daten-Synchronisierung “auf Knopfdruck” ist eine äußerst leistungsstarke Technologie für Geschäftsanwendungen. Als Entwickler können Sie einfach mit einer lokalen Datenbank arbeiten und alle Änderungen, die Sie in der Anwendung vornehmen, in der lokalen Datenbank speichern. Für einen neuen Anwendungsfall muss nur der neue Datentyp definiert werden, der automatisch gespeichert wird. Die Daten-Synchronisierung ist unabhängig vom Datentyp; beliebige Daten können synchronisiert werden. Für einen neuen Datentyp muss keine neue REST-Schnittstelle implementiert werden. Dies beschleunigt die Client-Entwicklung. Die Schnittstelle zum Backend-System ist die Datenstruktur, die sogar typsicher behandelt werden kann, wenn TypeScript verwendet wird.

Der Event-driven Ansatz

Die Kommunikation mit dem Server erfolgt asynchron. Direkte Rückgabe- oder Antwortcodes können nicht verwendet werden. Die Event-driven Architektur basiert auf Events bzw. Ereignissen, die verarbeitet werden. Das Ergebnis ist wiederum ein Ereignis. Falls das Ergebnis ein Fehler ist, kann der Fehler als Ereignis an den Client zurückgegeben werden.

Die Frage ist, wie beispielsweise ein Befehl wie “veröffentlichen” in einer Offline-First-Anwendung ausgeführt wird. Ein Ansatz besteht darin, das Command-Pattern zu verwenden und Aktionen auf der Serverseite auszulösen. Der Befehl “veröffentlichen” ist Teil des JSON-Dokuments, zusammen mit den Daten selbst. Das Ergebnis der Operation kann ebenfalls im JSON-Dokument gespeichert werden. Das “Ergebnis” ist ein Ereignis des gleichen Dokuments, das empfangen wird. Das Ergebnis der Operation ist im Dokument enthalten. Zum Beispiel kann bei der Einreichung eines Reisekostenformulars der Status “senden” sein, solange es noch lokal gespeichert ist. Sobald das Dokument mit dem Backend synchronisiert und daher vom Backend-System “empfangen” wird, kann der Status von ‘gesendet’ oder ’empfangen’ zu ‘verarbeitet’ geändert werden, sobald die Daten vom Backend-System verarbeitet wurden. Das einzelne JSON-Dokument enthält daher immer alle erforderlichen Daten sowie den Status und alle Nachrichten und Metainformationen. Der ereignisgesteuerte Ansatz passt perfekt zur Redux-Technologie im Client und zur JMS-Technologie im Server.

Fazit

Eine Offline-First-Anwendung profitiert nicht nur in reinen Offline-Szenarien, sondern bietet auch andere Vorteile wie eine bessere Benutzererfahrung und weniger Aufwand bei der Implementierung individueller REST-Schnittstellen. Allerdings erfordert ein Offline-First-Ansatz auch asynchrone Kommunikation, die von Ihrer Anwendung verwaltet werden muss. Dies kann durch die Kombination der Offline-First-Anwendung mit einer Event-driven Architektur erreicht werden und die Grundlage für eine zuverlässige und robuste moderne Webanwendung sein.