The prevalent MVC architecture diagram component has three objects and six interactions (Fig 1), but as applications scale, the clean, linear data flow grows and becomes cluttered (Fig 2).

Typical “MVC” triangle. Models notify Controllers and Views with Observable events. Controllers mutate views and state as desired. Views can ask for state at any time, and push user actions to controllers. Angular simplifies and obfuscates with two-way binding.

Fig 2: Typical “Data Binding” mishmash in MVVM, arising from loose application architecture and planning. Notice every arrow is two-way, and a new arrow can be added at any time.

Flux eschews the free-for-all two-way data binding in favor of certain well-defined push mechanisms. In flux, there are four parts to the pattern, each with one incoming and one outgoing data path, for a total of four interactions (fig 3). The pieces are Actions, objects that encapsulate a requested change in state; the Dispatcher, a utility that manages the data flow; Stores, containers and single sources of truth for application data; and Components, reusable and composable UI elements that make the view. In a complex application, the data flow goes from an application with numerous interdependent parts to one with many decoupled parts (fig 4) — any piece can be replaced with a few number of refactorings.

Fig 3: Basic Flux data flow. Dispatchers call Stores with Actions. Components observe stores, and create Actions. Data flows one direction, and one arrow may be active at a time (no new actions while mutating).

Fig 4: Flux data flow in a complex application. (The dispatcher is a singleton.)

When the Dispatcher component handles an Action, the dispatcher guarantees no other Actions are simultaneously being dispatched. This eliminates a “mutation cascade”, where a user action triggers some state to mutate, that itself causes some other state to mutate. These cascades can be very difficult to reason about both as a developer who must find the bug in a middle algorithm, or as a user when disparate pieces of the application all change together. Mutation cascades can also commonly have race conditions: either one section of data must be updated before another, or two sections deadlock waiting on the other to finish. In the case that order needs to be preserved between action handlers, the dispatcher has a waitFor method that will ensure correct sequencing events.

How does this all fit together?

The four data paths in the original Flux implementation.

When the user fills in a new ToDo item in the input and hits ‘enter’, the Component asks the ToDo Action Creators to make a new Action. The Action Creators do so, and pass a TODO_CREATE action to the Dispatcher. The Dispatcher iterates any stores with a registered dispatch handler, passing the TODO_CREATE action to the handler. The ToDo store’s handler looks for the TODO_CREATE event, updating its internal collection of ToDos. After updating the ToDo store emits a Change event, which the ToDo component listens to. Upon receiving the Update event, the ToDo component rerenders, and the application is in a stable state.

Song Flux

Multiplex by ActionConstructor

Song Flux provides the Dispatcher component of the Flux architecture, with some modifications. In the original Flux implementation, Actions were a tuple of (const string action, object data). In Song Flux, Actions are instances of Javascript Constructor Functions. Every type of Action is a function, which will be instantiated with the new operator when created by a component. Stores register not for all dispatched actions, but individually for each type of action the store is interested in receiving. This approach greatly reduces boilerplate overhead of setting up Flux data flows, and does not reduce flexibility or power of the approach.

Unidirectional Data Flow

Like the original Flux implementation, Song Flux enforces single action at a time in the Dispatcher. Actions are always dispatched within a $digest loop, either triggered by user action, like an ng-click handler, or as the result of some asynchronous callback, like an $http return. The dispatcher verifies that within an action dispatch, and within a $digest loop, only a single Action is generated. By preventing multiple actions per $digest developers cannot hit the frustrating situation of a mutation cascade, which will often result in the dreaded $infDigest error.

Song Flux does encourage “traditional” Angular MVVM architecture within reusable components of a web application. The benefits of two-way data binding have demonstrated themselves time and again in the Angular ecosystem. The failing is only when it comes to scaling an application beyond a couple interacting data sources and views.

Single Actions object

To consume Actions, the Action constructors must be injected in two places: Stores, for registering an Action handler, and Components, to instantiate and dispatch an Action. Actions can be created and injected individually; however, it was found to be much simpler in practice to combine all Action constructors for a part of the application (generally at the Store module level) into a single injectable Actions object, with each constructor being a property of that object.