DEV Community

Voltra
Voltra

Posted on

How the Command pattern can save your offline-first app's ass

Table of Contents

Preamble

Lately I've been watching quite a few Free Monad and Scala/Akka videos. I like videos about functional programming because they touch an area of mathematics I haven't had the chance to study: Category Theory.

And if you're like me, a front-end developer, you know that most of the "interesting" UX ideas come from the brilliant mind of the people working on (the mobile versions of) porn websites.

At work, we're making a custom tailored CRM. And one of the things that could be interesting, is offline support for the days when the internet cuts off. And this, as it should, had me scratching my head finding ways to keep actions going correctly locally (with a message that shows the user currently doesn't have an internet access).

And then, I remembered one thing. One of the porn websites I visit has this interesting feature where you can like videos and add them to your favorites even if you're not logged in. And when you log back in, you get notified that N videos were added to your favorites and likes.

And it just clicked in my head: commands.

Command DP

Example UML diagram of the Command Design Pattern where a Broker has a Order command type that has two subtypes, namely BuyStock and SellStock

The basic idea behind the Command Design Pattern, is to not sequentially execute actions, but rather represent actions as object (often with their associated inputs/parameters) that can be used/reused to actually execute said action.

What does all this means? If you don't like immutable data structures or extreme RAM consumptions (pleonasm?), you can implement undo/redo as a list of commands, an initial state and a current state.

How does that help?

Most people try to have front-end state be a 1-1 mapping of the DB. One of the easiest way about it is to use a front-end "ORM" like vuex-orm.

But then you have to think about what state is "fresh" and what is "uncommitted", how to send it back, etc...

Then you also have to deal with dirty reads and lost updates, etc... Things we would rather not have to worry about on the front-end.

Using commands (or messages if you're familiar with queue-related thingies) will greatly help with that:

  • Restructure everything to use commands
  • Have a OnlineExecutor and OfflineExecutor
  • Have the OnlineExecutor execute actions immediately
  • Have the OfflineExecutor queue the commands until the internet connection is back
  • Execute the queued actions when the internet is back (eventually by sending them to the OnlineExecutor?)

That will help you solve two issues:

  • How to separate online execution from offline execution
  • How to keep track of the offline work to apply when you're back online

What are the advantages?

It decorrelates intent from execution. Your commands represent all the system should know about what is to be done, and they have nothing to do about how to do the work itself.

The commands can be trivially serialized and sent over the wire so the work can be directly processed by the server.

And since it decorrelates intent from execution, it could even help you manage your GDPR compliance by just not sending messages to your analytics service if the user disabled that.

You can also embed information in your messages that, for instance, would require to chain calls to multiple REST API endpoints.

What are the inconvenients?

You'll either have to rewrite everything to use that logic, or make a "translation layer" which might not be pretty.

The number of type of messages can grow quite fast. Some developers might be tempted to reduce that factor by sending chains of smaller messages (which in terms of Functional Dependency means you're doing something wrong and should just create a message that group/chain these actions together, maybe allowing optimizations on the execution side).

Latest comments (0)