My name is Øystein Hallaråker and today I’m going to give you an overview of how we utilize the Flux application architecture in the Delve client.

In the Delve engineering team we focus on building for scale. Both in terms of code and developers. We want to avoid what typically happens to a software product over time:

Chaos!

We have found type safety and demand loading to be super important to achieve this. Equally important is having a consistent, simple architecture where the complexity of our code base does not increase as we add more features. Over the last couple of years, we have had more than 70 engineers contribute to the Delve client side code. We want any of these developers to go into any feature and feel familiar with how the code is structured and how data flows through the system. Given our requirements, Flux has been a perfect fit.

What the Flux?

Flux is the application architecture that Facebook originally used together with React. It’s not actually a framework or library but rather a pattern based on a unidirectional data flow, as shown below:

The biggest advantage of this architecture is a super simple mental model that we can apply consistently across features.

As this is just a pattern, there are several frameworks implementing the Flux architecture. We are not currently using any specific framework in Delve as we, from the start, did not want to box ourselves into a corner. In the next sections I’ll go through how we have implemented each part of the Flux architecture.

Data Layer

In the diagram above, the “Web API Utils” represents our client side data layer. It talks to the server, indicated by the “Web API” box. Our data layer is inspired by the great superagent library and we designed it to be super simple with a single responsibility: doing network requests and returning strongly typed responses. Important principles for us in this layer have been simplicity, uniform error handling across features, no caching, and monitoring by default. Below is an example from one of our data layer providers:

Actions

Actions are at the heart of Flux. To cause any change in the application state, an Action needs to be dispatched. In Delve, most of our Actions fall in one of two categories:

User Actions: click, type, scroll, etc.

Data Actions: Success or Error Action, when receiving a server response

All of our Actions implement the IAction interface:

We made an early decision that all our Actions should represent something that happened, rather than what we want to happen: “LeftNavPersonSuggestionClickAction” vs “GoToPersonPageAction.” We also wanted all our Actions to be very specific and to not reuse Actions across different user interactions with our app. We typically include the UI origin of the Action in the name. We have found that there are several advantages to this:

Usage logging for free (We have a Logger that logs all Actions)

Separation of concerns: When dispatching an Action, no assumptions are made about what state changes that Action will cause. Compare this to dispatching an Action named after what state change we want. What happens when we want to do another (different) thing when the Action is dispatched?

Action Creators

Action Creators are the glue in the Flux architecture. In Delve, all Actions are dispatched from Action Creators. Our Action Creators typically either dispatch user Actions, like a user click, or talks to our data layer and dispatches Success or Error Actions when the result comes back from the server. We decided early that we wanted each network request to result in a Success or Error Action being dispatched. We never combine the result of multiple network requests to dispatch a single Success/Error Action. Simplicity and consistency are key. The Action Creators may also talk to Stores to determine if some information is already available in the client and it may do multiple data layer requests, either in parallel or in sequence. Below is an example where a document is being added as a favorite.

Dispatcher

In terms of dispatching Actions, we are using the Facebook Flux Dispatcher. The singleton Dispatcher is super simple: Anyone can register a callback, and when an Action is dispatched, all callbacks are invoked.

Stores

Our Flux Stores hold all application state and each Store is responsible for a single domain. The domain is typically either some model or a section of the UI. Each Store registers with the Dispatcher and listens for Actions, changes its state and emits a change event that React Components listen for. One of the great things about using TypeScript is that we get type safe Action processing in Stores:

There are a few important principles we try to follow for Stores:

All state should be in Stores, no exceptions!

No side-effects. Stores only change their own state

Prefer many small, single-responsibility Stores over fewer big Stores

No dependencies between Stores

Components

We try to keep most our React Components dumb. In addition, we have some utilities for getting rid of the boilerplate code typically related to registering with Stores and rendering when Stores change. We also ensure that all Stores get to process an Action before Components start rendering. Have a look at BaseComponent and SmartComponent in Petar’s great webpack-react-flux starter-kit. Here’s an example from the starter kit: