Layers over hierarchies
User interfaces are hierarchical in nature, partly because HTML is inherently hierarchical and partly because of the way that we structure the information presented to users. For example, this is why we have nested levels of navigation in some applications—we can't possibly fit everything on the screen at once. Naturally, our code starts to reflect this hierarchical structure by becoming a hierarchy itself. This is good in the sense that it reflects what the user sees. It's bad in the sense that deep hierarchies are difficult to comprehend.
In this section, we'll look at hierarchical structures in frontend architectures and how Flux is able to avoid complex hierarchies. We'll first cover the idea of having several top-level components, each with their own hierarchies. Then, we'll look at the side-effects that happen within hierarchies and how data-flows through Flux layers.
Multiple component hierarchies
A given application probably has a handful of major features. These are often implemented as the top-level components or modules in our code. These aren't monolithic components; they're decomposed into smaller and smaller components. Perhaps some of these components share the smaller multipurpose components. For example, a top-level component hierarchy might be composed of models, views, and controllers as is illustrated here:
This makes sense in terms of the structure of our application. When we look at pictures of component hierarchies, it's easy to see what our application is made of. Each of these hierarchies, with the top-level component as their root, are like a little universes that exist independently of one anothers. Again, we're back to the notion of separation of concerns. We can develop one feature without impacting another.
The problem with this approach is that user interface features often depend on other features. In other words, the state of one component hierarchy will likely depend on the state of another. How do we keep these two component trees synchronized with one another when there's no mechanism in place to control when state can change? What ends up happening is that a component in one hierarchy will introduce an arbitrary dependency to a component in another hierarchy. This serves a single purpose, so we have to keep introducing new inter-hierarchy dependencies to make sure everything is synchronized.
Hierarchy depth and side-effects
One challenge with hierarchies is depth. That is, how far down will a given hierarchy extend? The features of our application are constantly changing and expanding in scope. This can lead to our component trees growing taller. But they also grow wider. For example, let's say that our feature uses a component hierarchy that's three levels deep.
Then, we add a new level. Well, we'll probably have to add several new components to this new level and in higher levels. So to build upon our hierarchies, we have to scale in multiple directions—horizontally and vertically. This idea is illustrated here:
Scaling components in multiple directions is difficult, especially in component hierarchies where there's no data-flow direction. That is, input that ends up changing the state of something can enter the hierarchy at any level. Undoubtedly, this has some sort of side-effect, and if we're dependent on components in other hierarchies, all hope is lost.
Data-flow and layers
Flux has distinct architectural layers, which are more favorable to scaling architectures than hierarchies are. The reason is simple—we only need to scale components horizontally, within each layer of the architecture. We don't need to add new components to a layer and add new layers. Let's take a look at what scaling a Flux architecture looks like in the following diagram:
No matter how large an application gets, there's no need to add new architectural layers. We simply add new components to these layers. The reason we're able to do this without creating a tangled mess of component connections within a given layer is because all three layers play a part in the update round. An update round starts with an action and completes with the last view that is rendered. The data-flows through our application from layer to layer, in one direction.