The Observer Pattern for Better UI Architecture

Today I’m going to show you a major pattern that underlies all good UI architecture and why you should learn it so you can become a better UI architect or engineer.

Theory

Whenever I do training, I like to wear two hats. One is called ‘theory’ the other is called ‘practice’.

As a non-guru type teacher who prides themselves on not sitting in an ivory tower, but actually having ‘applied’ the theory we teach in our courses in commercial contexts, I like to put my ‘practice’ hat on as much as possible.

Encapsulation

The first thing I’d like to draw to your attention is the often-forgotten idea of encapsulation. Back when I was a C# engineer this word was common because the static nature of C# gives you the ability to ‘hide’ information.

It seems forgotten in the JavaScript world; this word does not seem to get used because;

  1. Software engineers think JavaScript is a functional language and that we don’t need terms like these from OO languages.
  2. Software engineers think you can’t encapsulate dynamic prototype languages because everything is available on the ‘god’ .__prototype variable.

Both of these are incorrect; the reality is that you ‘can’ encapsulate with JavaScript to the same effect as with any OO language, but you must be doing it using JavaScript’s natural encapsulation mechanism which is of course; the scope (the bit in between the brackets).

So, the first thing we need for good UI architecture is scope.

Timing

People who use JavaScript often end up loving it because they have no choice not to if they want to master it. There is but one serious language to develop web UI apps in, and that is JavaScript (or it’s cousin TypeScript).

So, loving JavaScript is really the only way to build great UI apps.

Because native JavaScript is a single-threaded language this creates an overhead that seems that makes it challenging to write scalable architectures with.

When writing software with multi-threaded languages it seems that the overhead of understanding them is lower because you only have to think in ‘one’ linear process.

However, in JavaScript using the same techniques can often give engineers problems like this…

  1. They let the user update the UI.
  2. They get some data from a backend.
  3. The code crashes because the order of operations between the data loading and UI updating fails.

We call point 3 is a ‘timing issue’!

Because JavaScript has only one thread we can’t wait for responses on that thread as it will ‘hang’ the app.

Instead we must instead use callbacks; but callbacks never happen when they are passed because of the event loop (https://www.youtube.com/watch?v=8aGhZQkoFbQ). This means that engineers will often get timing issues and the code can easily fall through in runtime even though you might not spot it happening at coding time, thus;

The second most important thing in JS UI architecture is timing.

Patterns

The observer pattern can help us get predictable scope and robust timing.

Think of the observer pattern like the UN (United Nations).

The UN is something that countries have a membership of. They are affected by it; and need to know what is happening inside it. The problem is; the meetings and decisions that happen in the UN do not normally happen within the member country; and they are not under any of their own control structures either. In other words, the member country and the UN have different interfaces.

The way countries solve this problem is to simply send an assigned individual to the meetings and then have them report on progress and feed the wishes of the member country back into the UN panel.

Countries normally call this person a delegate.

The observer pattern works in the same way. It lets any module in our app send a delegated function to be called later on. This happens when the data has an update or something changes. It also lets any module issue a broadcast request to the observer; so that all other delegates are updated.

There are 3 constituent parts to the observer pattern;

  1. A boxing operation which lets us store underlying data of which we need to update and be notified of updates.
  2. A list of functions (delegates) which will be called when the underlying data is changed.
  3. A broadcast or ‘notify’ function which can be called which will update the delegates, this function can be called internally or externally.

From an implementation perspective we can reduce the observer pattern down further into two simple concepts.

The boxing part and the subscription part. Which I highlight in a slide from our UI Architecture Academy here…

It works by allowing us to ‘wrap’ an original value inside the observable wrapper (the code on the left). This creates a logical ‘box’ around the underlying value. We can intercept changes to this value and then broadcast them using the code at (bottom left).

You can see that the underlying pattern is relatively simple and can be implemented in 25 lines of code.

But be warned…

Real World

About 5 years ago an engineer at a famous San Francisco based networking company presented me with a problem.

He told me that we couldn’t write a test for the login part of our app because we were forced by the current architecture to already call the login process somewhere else. This means we could never know for sure when the login was actually complete in the new feature. Moving the code simply reversed the problem.

When you build commercial UI apps at scale this sort of issue comes up a lot. It’s a timing issue which creates coupling, and coupling is the biggest challenge to writing flexible and testable code.

The observable pattern lets us solve this coupling and testing issue because it allows us to invert the dependency since we no longer have to directly call and wait for anything. We simply attach our delegate functions to a central process and they will be automatically called at the right time.

This approach began to radically change how we started architecting the app and really helped us solve some hard testing problems because it let us get a clear scope and also fix the timing issues, however over time our solution began to show cracks.

At first, we realised that we wanted more complex data structures but couldn’t fit them into the slim API in the example I have shown you. Then we realised we needed to chain the callback functions together because we needed more layers of scope.

We tried to grow this module but, in the end, we were limited. Since that day I do not recommend using roll-your-own observable implementations for major architecture in a UI app.

Instead; we teach engineers to use an observable framework because these frameworks come with a whole host of features that we will need over time as we grow our app. These features protect you against the sort of scaling issues that I got!

So, what is a good observable framework?

Mobx

I can recommend Mobx as the simplest and most scalable observable solution that you can use currently in a JavaScript app (and we have used a few).

We have used Mobx for many years and found that it enables complex UI architectures to be built. Unlike other observable frameworks its scope is limited to being good at doing updates and tracking state.

Some of the other ones have very complex streaming pipelines and functional operators such as RXJS, in my experience having too many of these options available gives the engineers rope to hang themselves with, but Mobx doesn’t so it’s great to use for that reason!

Conclusion

JavaScript engineers either forget, or are never made aware of original principles which are so common to multi-threaded languages like C#.

Encapsulation and timing issues can plague a JavaScript app because of its single threaded callback nature. This often manifests in coupling issues which get in the way of writing scalable and testable code.

So, to solve this problem; we can leverage JavaScript’s natural language feature (scope) alongside the simple pub/sub pattern the ‘observable pattern’, which lets us decouple our code.

As an engineer or UI architect you should be fluent in this pattern, and if you want to use it then Mobx is a great framework to use in complex apps that need to be kept as simple as possible!

Happy architecting ;-)


Author: Pete Heard

Pete is the founder of Logic Room. He designs the companies core material for JavaScript architecture, test automation and software team leadership.

LinkedIn

Want to learn 5 critical lessons for framework-agnostic JavaScript UI app design and structure?

Download our 28-page book exploring big picture ideas to help you create better architecture with any UI framework (inc. React, Angular or Vue).

Download
Close

50% Complete

Book Trial

Please fill out your details if you would like to trial our system.