Post-MVC part 4: Enter the Flux

Intro

Last week we discovered that Component based applications are trees. We saw that Components can only communicate one level up the tree and one level down. If a Component wants to communicate with his 'siblings' he will have to to that via his parent.

We also learned that the state of the application resides in the Components, but that it is not always apparent where the state should live.

The intercommunication between Components and the location of the state can make Component based applications complex. This week we are going look into a architecture called 'Flux' in order to see how this complexity can be tackled. 

Enter the Flux

React is a Component based View library from Facebook. It popularized the Component based approach and made it mainstream.

Facebook at some point discovered the weakness of traditional MVC and created a new way of managing state. They created Flux an architecture that uses a unidirectional data flow.

What the Flux

Traditional MVC is bi-directional which means that the Model, View and Controller can communicate with each other. This is also the same for the  Component architecture we saw last week.

Flux advocates having a unidirectional data flow, meaning that all state flows one way, from the top to the bottom. We still have a Tree of Components like before, but Flux will handle the intercommunication and storing the state.

Lets look at the various concepts in the Flux architecture.

Store

A Store contains logic and data and acts as the 'store' for a particular domain. For example in a Todo MVC application we would have a store for  the Todo's. The TodoStore would then know how to add a Todo and keep track of the list of Todo's.

By having a Store the location of the 'state' is very transparent. The need for a particular Component to internalize the state is no longer needed. The decision we had to make last week between storing the todo's in either the TodoList or the TodoApplication is moot.

Multiple stores can be made for the various entities in your domain.

Stores register themselves to the 'Dispatcher' to receive actions which originate from the views. Lets look at the views before looking at the Dispatcher.

View and Controller-Views

Flux has two types for View's the: regular View and Controller-Views. Behind the scenes they are simply regular Components the distinction is in their behavior. 

The normal views are just regular Components representing a kind of graphical widget. The Controller-Views are views that register to the various stores to receive callbacks for when the data in the store changes. Their jobs is to provide the normal views with the data they need to correctly render, they help keep the normal views easy to understand.

For example in a Todo application we have a TodoListComponent which is a Controller-View, it registers itself to changes from the TodoStore. The TodoListComponent in turn renders TodoComponents which are regular View's. The TodoComponent will be composed out of multiple other subcomponents which are regular views. The TodoListComponent is more complex, as it listens to the store, but helps keep the other sub components plain and simple.

So a Controller-View helps render the state from a Store. But how is the state from a store manipulated? The answer is via Actions, which are created when an event occurs on a view, for example when a button is clicked or an input field is filled in. The view will then dispatch an action to the dispatcher.

Dispatcher

The dispatcher job is to receive the actions and route them to the correct Stores. The Stores register themselves to the dispatcher so they can receive actions via callbacks. A Store can then act on a particular Action.

Note that there is only one Dispatcher per application. You could say that it is a singleton.  The Dispatcher itself contains no business logic, its job is to simply route and receive actions.

For example a view could dispatch an AddTodo action which is a simple object such as:

{
  type: 'ADD_TODO',
  text: 'Buy groceries.'
}

The dispatcher will inform the stores via callbacks. A TodoStore could listen to this event like so:

var todoStore = [];

Dispatcher.register(function(action) {
  if (action.type === 'ADD_TODO') {
    todoStore.push({complete: false, text: action.text});
  }
});

In the view (Component) which represents the list of Todo's the Todo will then be added.

Benefits of Flux

One benefits of Flux the 'action' is decoupled from the 'store'. This makes it possible to send the same action, to the dispatcher, from different Components, whilst still performing the same action. 

Imagine that in our Todo application there are three ways to add a new todo:
  1. A new Todo input and button, aka the normal way to add a todo.
  2. A duplicate Todo button next to an existing Todo.
  3. A group of buttons for adding predefined Todo's for quickly adding todos.
These three Component will all send the exact same action to the dispatcher. The TodoStore will add the new Todo to the array when the event occurs. The TodoStore is not aware of the origin of the action, the Components sending the actions are not aware what happens when the actions are
received. In other words the producer of the action is not coupled to the consumer of the action.

Because they are decoupled we get some freedom to refactor the code of the producer without affecting the consumer or vice versa. In the example the todo's were kept in an array. If for some reason we would  want to keep them in another data-structure we would change the Store, but not all components listening to them.

This also works the other way around. If we decide that the duplicate Todo button is not useful we can remove it without having to update the store, or the dispatcher.

Conceptually unidirectionality is easier to understand too. The constraint that 'state' flows down makes it easy to reason about where the state came from. When you are working on a Component such as the TodoComponent you know that the Todo itself came from above.

When you want to trigger and event from a Component you simply dispatch it via the Dispatcher, you do not have to worry about how to 'reach' a  cousin component. No 'indirect' relationships in the Tree of Components exist, such indirect relationships make understanding the whole application more difficult.

Variations of Flux

When Flux was announced by Facebook they explained the idea of Flux but did not provide an actual library or framework for using Flux. Flux was so compelling however that multiple implementations started popping up. Such as FlummoxAltReflux etc etc.

Each variation would do things slightly differently compared to the original Flux architecture. But soon a new contender for the crown entered the stage: Redux.

It took the ideas of Flux and improved upon them, it is currently the most popular Flux derivative out there, next week we will discover why.