Flux Pattern Supercharged Published on

Modularity and Composability

Modularity and Composability. This is what you will read about here - when applied correctly, you can take advantage of them and extend your software easily with even greater abilities.

Flux (Redux) Pattern

Flux is an architectural pattern developed by Facebook to manage unidirectional data flow in applications. It consists of four main components:

There are many different implementations, such as Vuex (Pinia), NgRx, and Redux. Despite some differences, all implementations share a core set of similar concepts and mechanisms that interface with the process’s key steps.

In this post I want to lay my focus on following things:

To highlight these points in a (fictional 😁) real-world scenario, we will walk through an example showcasing the true potential of a well-designed system.

Think of the most generic customer/client/todo list application, which is already built in a good manner, we used the flux pattern from the beginning on and also transformed the whole frontend into a PWA, so we have everything set up.

This is how our starting point graphically looks like.

The Requirement

After business decided we need to extend and integrate offline capabilities into our existing application. The user should be able

Are you online?

The first thing we probably ask ourselves is how to determine if we are offline or not. Navigator.onLine looks promising, but don't be fooled so easily - determining whether you are online or offline is highly context-dependent. Maybe you deem yourself online when you can reach something in your LAN. Maybe you take an external server on the internet as your online checkpoint? It really depends...

Whatever it will be, we will be packing that logic into it's own service, as you can see on our new component diagram.

Temporarily saving changes

Since we need to save our changes somewhere (while we are offline) the browser landscape offers us different options:

Since we are working with more complex objects and also need offline support we decided on using IndexedDB.

Of course those offline changes will be synced later on ..

Taking first steps without stumbling

Without spending too much time, we just typed away - I mean we already know whether we’re online or not and where to save our local changes.

Our first step was to split the offline and online behavior in our Action like this:

addCustomer(Customer c);

addCustomerOffline(Customer c);

In our components we call the appropriate method.

if (connectionDetector.isOffline()) {
  addCustomerOffline(c);  
} else {
  addCustomer(c);
}

In the offline variant the customer change is getting written into the IndexedDB and for the online case the HTTP API is getting called (which in turn updates our local data in the IndexedDB).

Refactoring towards clearer insight

We tested our changes and everything is working fine, opened a merge request only to getting it back thrown at us. Our Senior Developer said there would be way more potential in improving the structure and readability. He showed us ..

We can employ the State/Strategy pattern to enhance modularity and composability. We will handle the offline and online state internally in the Action, meaning we will have one online and one offline Action State. Both implementing the same methods.

This way all clients of our Action don't need to care about whether we are offline or not (and thus shifting the responsibility of it away from the view). Moreover we unified the handling of the operations.

This results in our Action getting narrowed down to the original again:

addCustomer(Customer c);

And our components? Only calling it like this:

addCustomer(c);

Here is an updated view of our components.

The Action will get notified about connection changes and changes it's internal behavior respectively. Everything opaque to the client of the Action.

This also reduces the dependency from the view to the connection detector (not all since there will be cases where the view needs to update the UI depending on the connection state).

Syncing our local changes

Our senior developer was happy, and we got our changes approved.

One last change needs to be made to finish up the offline capabilities: syncing data when we’re back online.

The method for doing this should be quite straightforward since we need to take the saved data from our IndexedDB and apply those changes to our HTTP API.

One possible mechanism we could use is the Background Sync API, though other options exist. The changes can be made directly (in real-world scenarios there’s probably some additional synchronization logic involved) to the HTTP API, or through our existing Action. The most important thing is that we use our connection detector to get notified when we’re back online again.

This leads us to our final component diagram.

Summary

We extended our existing PWA into a full-blown offline-usable app and looked at the reasons behind our custom connection detector.

By employing the State/Strategy design pattern, we experienced the advantages of composability and modularity, centralizing offline and online behavior in one place and making our components way clearer (readability). Moreover this also lets us focus our components on their responsibilities.

Finally, we designed a solution for syncing saved offline changes back to our API.

Of course, in the real world there will be a lot more things to consider, in practice, it always works differently, but having those utilities and knowing when to use them for good will give you a great advantage.