Goeleven.com

Store and Forward

S

When building an offline capable PWA on top of a CQRS style service, you'll need different approaches to handle network drops for both the read and write side.

On the read side you can rely on the cache aside pattern to deal with an unreachable query api.

When issuing commands, a different pattern is required though. One such pattern is store and forward.

The idea behind store and forward is:

  • If a request, representing a command, fails due to network connectivity problems.
  • It gets stored locally in a queue instead.
  • When the connection restores, the failed requests, which are stored on the queue, will be forward to the service.

This pattern is made possible in PWA's by combining

  • Service worker to intercept the POST and PUT requests and check the result of their invocation.
  • IndexedDB can be used as queue storage for the failed requests.
  • Background Sync: The background sync API allows the service worker to submit any queued requests whenever connection gets restored, even if this happens after the website has been closed. Note that only Chrome, Edge and Opera currently support the background sync API. In other browsers the queued requests can only be submitted the next time the user opens the app again and the service worker gets restarted.

Store and Forward

Workbox

Luckily this pattern has been implemented already in the google workbox library and can be added to your PWA with ease.

Similar to how we made our app shell offline capable, it boils down to importing workbox in our service worker sw.js and configure it.

The BackgroundSyncPlugin registers itself on the fetchDidFail lifecycle callback and will add any failed request, for the route it is attached to, to the background sync queue stored in indexeddb.

It will automatically retry them when future sync events are fired.

importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.0.2/workbox-sw.js');

if (workbox) {  
const bgSyncPlugin = new workbox.backgroundSync.BackgroundSyncPlugin('bg-queue', {
 maxRetentionTime: 24 * 60  // Retry for max a day (specified in minutes)
});

workbox.routing.registerRoute(
  /.+\/api\/.+/,
 new  workbox.strategies.NetworkOnly({
   plugins: [bgSyncPlugin]
 }),
 'POST'
);

workbox.routing.registerRoute(
  /.+\/api\/.+/,
 new  workbox.strategies.NetworkOnly({
   plugins: [bgSyncPlugin]
 }),
 'PUT'
);
}

Challenges

Using the store and forward pattern, together with the cache aside pattern, allows about any app to become offline capable.

It does present some new challenges though, or better, it aggravates challenges already present in the CQRS model.

In a CQRS system, the service is in control of decission taking.

The client sends a command, but is unsure of the outcome until the service has taken its decission and communicates that change back to the client.

The direct return path for these commands has been cut off. When a command gets stored locally and only forwarded after the user closed the app, the user will have no knowledge of any information in the response. Http response codes just became meaningless and need to be replaced by other communication paths, e.g. an email.

Combine this with the downsides of the cache aside pattern, where a user is seeing stale data, he or she might assume that the command hasn't been issued at all and will repeat it a few times over, leading to duplicates.

This pattern will impact the productivity of the user as well, as time to feedback increases, the process that the user is following will slow down and uncertainty will increase.

Needless to say that going down this path will require drastic changes to the user experience over an always online app.

I do recommend this pattern for any capability where you cannot trust the user, e.g. online shopping.

For scenarios where the user can be trusted, e.g. when they are working on their own data, there are better ways to make apps offline capable.

About the author

YVES GOELEVEN

I love to build web and cloud apps and have been doing so for the past 20 years.

My main areas of expertise are progressive web applications, event driven architecture, domain driven design, event sourcing, messaging, and Microsoft Azure.

Through this blog I hope to share some of this experience with you.

Get in touch

Sign up to my newsletter to get notified about new content

You can unsubscribe at any time by clicking the link in the footer of your emails. I use Mailchimp as my marketing platform. By clicking subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.