Forget about react/angular/flux/redux, design your app first — part 1/3
Part 1: architecture and the view component
In this article, I will show you how to use a few lines of code (≈160) to build a UI-library-independent front-end application, but still benefit from all the features of redux pattern.
You can play with it at: https://bhou.github.io/ui-architecture-demo/
The source code can be found at: https://github.com/bhou/ui-architecture-demo
Here is the architecture diagram, generated directly from the application with collar.js and collar-dev-server:
I am a traditional computer science background person. My education and experience make me always design the software first, and pick the right tool/library to build it.
Let’s take front-end for example, you can find tens of frameworks or libraries to help you build your applications: React, Angular 2 (and 1), Vue.js, Flux, Redux, Redux-router. You can also find lots of boilerplate projects in github (for example, the famous create-react-app project made by facebook) to help you start your project with a proposed project structure. Once you dig into any of these libraries, you will find they have totally different mindsets and styles to make things done. If you adapt your code to one of them, it almost means that saying goodbye to others. (.jsx for react, .vue for vue.js, .ts for angular …)
The worst comes with flux and redux, they are just two simple design patterns, but people starts to treat them as a framework, a new technology, and there comes thousands of how-to articles to teach you how to make apps with them.
I even saw a new job title: react/redux engineer recently.
In my personal opinion, this is too crazy. Shouldn’t we just forget about all these libraries and focus on designing the architecture that adapts to the business need and then pick one of these libraries that fits well to our design?
Here comes this article: building a counter app with software architecture design first, and then integrate different libraries into it.
Let’s start from the architecture
Although we will not think about using these libraries from very beginning, the great ideas behind of these frameworks/libraries can help us to design the architecture. In this article, I will borrow the idea of Flux and Redux. Flux gives us a good way to model the data flow (unidirectional data flow), and redux shows us a good pattern to manage the application state. Our architecture contains two top level components: view and store.
The following diagram shows the architecture: (it is directly generated by collar.js and collar-dev-server)
Our view has two responsibilities:
- render the UI with data
- accept user interaction and send action
The arrow line points to view component shows that the store can push data to the view for rendering, the arrow line points from view to store indicates the action emitted by the view (usually triggered by a user interaction).
The store handles the business logic (by handling the action emitted by the view, and push new state to view for rendering when state changes)
In the first part of this article, I will focus on design and implement the view system. We will cover the store component in the part 2.
Now we have our design, let’s build an abstraction layer to describe it:
For the view, we can create an ES6 class to represent the interface:
It has 4 methods for the subclass to override:
- render(state): takes an object as argument to render the view, you need to add UI event listeners in this method and call
this.sendin the listeners to emit actions
- update(state): update the view, this is useful to refresh part of your view
- addActionHandler(actionType, handler): register a handler for a special type of action
This abstraction is trying to have a view interface independent to any controller implementation. You can use the redux-like store component (in our design) as the controller or you can plug-in the business logic directly to the view with
To build your application with this view abstraction, you just need to implement a subclass, implement the
update method, and use
To use it in your application, simply put the following code:
We update the view in action handler by calling
Your app looks like this:
You are a react fan?
If you want to use react for the view, it is very easy, just subclass it with react implementation, here is an example:
We pass the view instance (this) to the “view” property, so that our react component can use it to emit actions (by calling
Make the view component data flow visualisable
To go further, you can implement the abstract view class with collar.js and visualise your view’s data flow with collar dev tool.
Collar.js is message driven. For our view component, it handles two messages:
update . Each takes the state as message body. We build a
CollarView class to represent it.
In the constructor, we build the following data flow by creating each node and wiring them up together:
We have a different implementation in
render methods: Instead of leaving the implementation of these two method to subclass, we send a message (render or update) through the input node (the top left one), so that it will trigger the corresponding pipeline (render or update).
Two new methods are added:
setUpdater . A
renderer or an
updater is a function with the following signature:
function renderer(state, done)
function updater(state, done)
They take a state object as the first argument, and call
done callback when update/render is done. Renderer and updater is called in the render the view node and update the view node.
A sensor node is created to help the view send actions. In the
send method, we simply delegate the action-delivery job to sensor. When you add action handler to the view, an action pipeline is created and connected to sensor node.
And create our application with it:
Now you can see the data flow from collar dev server :
The top is the data flow (pass state to view) from controller to the view in order do the rendering and updating. The bottom is the action flow, starts from the view (sensor), and flows to controller. (in this example, the controller is integrated in the view, as the INCREMENT and DECREMENT branches)
In this article, we designed a view interface, and provided two abstract view classes, one based on simple event emitter (≈30 lines of code); another based on collar.js (≈80 lines of code) to give us the possibility to visualise the data flow.
The design of this view interface makes it independent to any controller implementation. You can even plug-in your business logic in the view with action handlers.
In next part, we will design the store component, and provide a redux like API to help you manage your application state.