Flux and Presentation Architectures

Chris Trevino
ATS Blog
Published in
7 min readJan 25, 2016

--

As software engineers, one of the things we strive to do is make software that adheres to some elegant, reusable patterns that allow it to grow well with additional features and team members. At ATS, most of our server-side software consists of RESTful services, and implementing stateless service-side code is a well-understood problem. Our client-side architectures have been more interesting.

Architecting a Javascript application is a complex task. Compared to other development environments, much less structure is imposed on you. Everything from build-management, package-management, transpiling, linting, testing, minifying, bundling, etc. involves a far-reaching decision from a wide array of options.

Also, Javascript UI Frameworks are in no short supply, and at ATS we have used several of them to ship working applications to our customers: Dojo, ExtJS, Angular, and most recently React.

Cyclopean Data Flows

In a post I authored about a year ago, I described a particular problem we ran into with Angular and state management. In particular, scopes were being used to inject data into child components, resulting in bizarre runtime bugs when rearranging components around the application. This inspired the Eldritch Flow:

Evil Elder God Data Flow

Around the timeframe of this post, Facebook announced Flux, proposing a new way of architecting user interfaces. It was an compelling idea, and one we’ve wanted to experiment with since then. The core idea is centered around a one-directional data flow loop, similar to a game loop, and externalizes application state and async operations away from view components and into a set of store components. In most of the user interface frameworks we’ve used, application state is spread across a series of view controllers, and controllers play some role in resolving asynchronous operations.

At the time of the announcement our thoughts were that Flux seemed like a reasonable architecture for huge applications like Facebook, but also involved a significant amount of boilerplate. Since the initial announcement and vanilla Flux implementation by Facebook, there have been several implementations of the Flux architecture, and many of them have been met with positive community feedback.

Presentation Architectures

The presentation layer of an application can be designed any number of ways, but the vast majority of UI software is designed using a Model-View-Controller (MVC) architecture, or a variant thereof.

MVC is a very broad term, and there are several MVC variants we’ve encountered and implemented. A common thread between all of these implementations is a “thinning out” of the Views, which are responsible for rendering. Views are the least testable, most volatile component in any application, and making it as passive as possible can dramatically improve the quality and predictability of an application.

Traditional MVC

The MVC pattern has been around for decades and is a staple of design for iOS and OSX software. In a traditional MVC pattern, there is a one-way data flow loop. The view emits user actions to a controller, which will update a data-model according to business rules, which will then emit a data change notification, triggering a re-render.

Traditional MVC Data Flow

Mediating Controller MVC

A slight variant of this places the controller as a mediator between the view and model, breaking the dependency between the model and View. This data flow is the standard MVC data-flow as described by Apple and Google.

Mediating-Controller MVC

Model-View-Presenter

The Model-View Presenter pattern is similar to the Mediating-Controller MVC Pattern, with the addition of data-binding between the view and model. This is a bit of a tradeoff from the Mediating-Controller MVC, in that the data binding eliminates some boilerplate for presenting simple properties, but muddles the separation of concerns between the presenter and the model.

Model-View-Presenter (MVP)

Model-View-View-Model MVVM

The Model-View-View-Model architecture, introduced by Microsoft, is a presentation pattern dependent on two-way data binding, and the concept of a “view-model”. In this context, the View Model is a model describing the state of a specific View, and interacts with a more general domain model via synchronous updates or events. This architecture is common in modern Microsoft systems, KnockoutJS, and later versions of Angular.

Two-way data binding, featured most prominently in the MVVM architecture, is a staple of modern user interface architectures. It’s strength is that it provides a vast reduction in state-syncing boilerplate, but it is also dependent on a rendering engine that understands when state changes.

Model-View-View-Model (MVVM)

AngularJS “MVC”

AngularJS 1.x was an interesting framework. It was a big improvement on existing web frameworks, but had well-known warts (scope, directive definition, modules) and an MVC implementation that was a hybrid between MVP and MVVM. Models in Angular were implicitly created by the system in Scope and developers interacted with models by initializing them in controllers and manipulating them from views. Models could be bound from views and controllers, and controllers were responsible for handling user events.

The “controller as” syntax was introduced to help reduce scope-related issues and let developers design controllers as class instances. This was a large improvement and changed the nature of Angular’s MVC towards being full-blown MVVM.

AngularJS 1.x (with Scope models)
Angular 1.x (with Controller-As Syntax)

Flux

The Flux architecture, introduced by Facebook, emphasizes a one-way data-flow and rendering loop. This was an intentional break from other modern presentation patterns that emphasize two-way data binding. One-way data binding eliminates the notion of a magical binding engine, resulting in predictable and debuggable rendering behavior.

This one-way data flow loop hearkens back to a traditional MVC data-flow loop with some additional invariants:

  1. Views use one-way binding, and treat data stores as immutable. They are synchronous. All state-modifying user interaction is represented as action objects.
  2. Actions, which are analogous to an MVC controller, resolve asynchronous interaction with a domain model, and dispatch results to stores. Actions must have a unique name and a payload.
  3. Data Stores, which are analogous to an MVC View or MVVM ViewModel, respond synchronously to events and manage the application state.
Typical Flux Architecture

There are several implementations of the Flux architecture, most of which adhere to the design prescribed by Facebook. One of the more interesting variants, Redux, simplifies this design a bit by eliminating the role of the dispatcher, consolidating stores into a single state-container, and placing invariants on state management:

  1. Application state is a global, immutable document. New application states result in new renderings. This is called Time Travel.
  2. New application states are created via Reducer Functions, which are pure, synchronous, and functional.
  3. In React, state is injected into React Components via properties. Ideally, component state is not used.
Redux Flux Architecture

The most elegant thing about Redux is how it defines presentation components in a functional way. Each component type is transforming one domain into another and these functional boundaries are enforced by design.

  1. View components translate application state into rendered DOM.
  2. Actions translate user intent into event streams.
  3. Reducers translate events into application state.
Redux Functional View

What Architecture Should I Use?

Engineering is about tradeoffs, not perfection. You should understand the ramifications of whatever presentation pattern you choose. The choice between Flux or an MVVM architecture presents a tradeoff between reduced boilerplate and predictability.

At ATS, we are excited about the potential of Flux and Redux. We believe that frameworks that actively promote best-practices to be superior to those that don’t. One of our biggest gripes with Angular was how their code samples emphasize easily-graspable code constructs that don’t adhere to best practices. Flux and Redux actively promote an application layout and code invariants that encourage best-practices.

The Redux Time Travel concept is ripe with possibility. With Time Travel, the application can completely be re-rendered from an application state document. This has obvious implications for development-mode debugging, but imagine this concept applied to integration testing, customer support, collaboration, or workspace persistence.

If you want to dive further into Flux and Redux, I suggest starting with Lin Clark’s excellent cartoons on the subjects: A Cartoon Guide to Flux, and A Cartoon Intro to Redux.

In my next post, I’ll discuss our experience with two different Flux implementations, Alt and Redux, and dig into some actual coding examples.

--

--