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.
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.
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.
So, the first thing we need for good UI architecture is scope.
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.
We call point 3 is a ‘timing issue’!
Instead we must instead use callbacks; but callbacks never happen when they are passed because of the event loop. 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.
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;
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…
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?
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!
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 ;-)
Download our 28-page book exploring big picture ideas to help you create better architecture with any UI framework (inc. React, Angular or Vue).