Building an Offline-First Application

JavaScript applications have gained significant popularity in recent years, notably through well-established frameworks such as Vue.js, React, and Angular, as well as the capability to develop extensive business applications with TypeScript. The utilization of browser engines allows these applications to be device-agnostic, requiring only a browser for execution and can also be offline.

This article explores the architecture of JavaScript-based rich clients, emphasizing the significance of the user interface for mission-critical applications. It also delineates advancements in web application development, particularly the shift towards an event-driven approach and an offline-first strategy.

Brief and Concise

  • JavaScript based frameworks for the browser are used for building enterprise single page web applications.

  • New JavaScript concepts like state management and local storage in the browser, leading to a different design of REST interfaces.

  • Offline capability is more than storing data offline – it requires further architecture concepts.

The combination of JavaScript running in a browser and therefore being platform-independent, TypeScript and mature JS frameworks results in larger business-critical applications that are very similar to “rich clients” based on Java Swing or 4GL programming languages:

  • Protected by authentication and authorization
  • Client-server communication
  • “Intelligent clients”
  • Capability of working offline
  • For enterprise applications: Use of multiple screens and Multiple modules / sub-applications

Besides the underlying technology, a major difference is the interaction with the server. The database connection of the old rich clients is replaced by the stateless REST communication.

JavaScript based rich client architecture

With business critical applications, users use these applications intensively. The UI needs to be highly optimized in terms of workflow, but also in terms of displaying the right information. The speed of responsiveness is very important. If you are navigating through the screens and making changes hundreds of times a day, you do not want to wait seconds for the next screen to appear. The next screen should appear immediately. This is achieved by keeping the state in the client and only transferring changes to the server in the form of JSON documents.

There has also been a big change in the way web applications are built. Moving away from server-side rendering and later using AJAX requests, REST communication replaces server-side rendering and only sends data to the web page. The JavaScript code then renders the content. From a user experience, this is an improvement because only the data needs to be sent to the client. This is faster than rendering the entire web page. With the new concepts of state handling and local storage in the world of JavaScript, data can be stored locally in the browser.

This allows for a different design of the REST interfaces and data handling. It results in a different type of client application. A progress web app (PWA), e. g. defines a set of technologies used to deliver not just a web application that runs in the browser, but a complete application that can be installed and run as a standalone application. A progressive web app is an app that’s built using web platform technologies, but that provides a user experience like that of a platform-specific app.

Offline applications

One element of a PWA application is its offline capability. The architecture pattern behind is to store data always locally and perform the communication asynchronously in the background. The application is mainly using the data available locally. The usage of the application is independent if a network connection is available or not, of if the network is limited. This allows not only the application to be used when offline, but also makes the application much faster to use. As the data is stored locally and therefore available, a response to a user interaction is directly answered with data that is locally available. The local data is stored in memory (e.g. Redux store) and index DB for long term persistence. As there is data on the client and data on the server, a synchronization mechanism is required. This means that changes on the client are send to the sever and the client receives new changes form the server. Normally a generic logic is used, which can take care of data synchronization independent of the type of data. If the data synchronization is based on JSON objects, only the most recent changes are sent. Older versions of the changed objects are no longer needed and are discarded.

Depending on the data and how offline is handled, there are several ways to synchronize data with a backend system: If you have complete JSON objects, every changed object can be sent to the backend server. An alternative is to send only the delta information (e.g. changed fields) or to use an event source based mechanism.

From a high-level perspective, having data available offline and doing all the data synchronization in the back end sounds simple – but it is not. When data is kept offline and is synchronized to a backend server, and the data is based on regular business entities used by multiple users, conflicts can arise, which must be handled by the application or the synchronization layer.

As data is processed asynchronously in the client, a queue mechanism can be used to manage the data processing. E. g. can an inbound and outbound queue decouple the sending and receiving of data from the stored data. It also decouples the persistence technology from the synchronization logic and allows specific implementation for persistence or encrypted data. Using a Redux store in a React-based application also allows the UI component also to react to changes in the Redux store.

Advantages of offline capabilities

Once a data synchronization mechanism is in place, in addition to the offline capability, it enables three scenarios as shown in the following figure:

  • UI updates when new data is received from the back end
  • Synchronization not only between a client and a server, but also between two clients (via a server)
  • Event-driven architecture by sending commands in JSON objects over the synchronised data objects.

The data synchronization mechanism described above, which allows offline use, is not suitable for all types of applications. There are applications where you want the changes to take effect immediately, such as user management, or if you have a control UI for a switch. Also, if you have an application where you need a lot of data, for example for reporting, then the effort to keep the data available offline and to synchronize the data is much higher.

Having a data synchronization at your fingertips is a very powerful technology for business applications. As a developer, you can simply work with a local database and save any changes you make in the application against to the local database. For a new use case, only the new type of data must be defined, which is automatically stored. The data synchronization is independent of the data type, any data can be synchronized. No new REST interface must be implemented. This speeds up client development. The interface to the backend system is the data structure, which can be even treated type-safe if TypeScript is used.

Event-driven approach

The communication to the server is done asynchronously. No direct return / response codes can be used. The event driven architecture is based on events which are processed. The result can be events again. In case the result is an error, the error can be passed on as an event back to the client.

The question is how to execute, e. g.  a “publish” command in case of an offline first application. One approach is to use the command pattern and trigger actions on the server side. The command “publish” is part of the JSON documents amongst the data itself. The result of the operation can be also stored in the JSON document. The “result” is an event of the same document, which was received, just with the result of the operation included. For example, if a travel claim form is submitted, the status may be ‘sent’ when it is still locally stored. Once the document is synchronized with the backend and therefore “received” by the backend system, the status can be changed to ‘sent’ or ‘received’ to ‘processed’ once the claim form has been processed by the backend system. The single JSON document therefore always contains all the required data, as well as the status and any messages and meta-information. The event-driven approach perfectly fits to the Redux technology in the client and the JMS technology in the server:

Conclusion

An offline first application is not only usefully when the application has to work in offline scenarios, but also offers other advantages like better user experience and less effort implementing individual REST interfaces. An offline-first approach also requires asynchrony communication, which must be managed by your application. This can be achieved by combining the offline-first application with the event-driven architecture that can form the basis of a reliable and resilient modern web application.