Abstraction layers and schemas and state management, oh my.
Re-architecting Apteligent’s web application, part 3
Last time I discussed our choice of Flux as the front-end architecture in Apteligent’s new web application. Today, I’ll explain the Flux-implementing technologies we’ve chosen to use.
Once again, here’s the diagram of data flow in Flux:
The best part of making architectural decisions is when some of them are made for you. Pretty much everyone who’s using the Flux architecture uses a single framework to build the views: React. React’s simple API, high performance, and component-based structure make it a pleasure to work with.
That’s one down. Now we need to find a framework or frameworks to handle the rest of Flux: the actions, the dispatcher, and the store.
In the beginning, there was a choice.
Over the past few years, various Flux frameworks have been released; but by the time we got to the scene, one had risen to the top of the heap: Redux. It came highly recommended by the creator of Flux herself, and was the framework officially recommended by Facebook for application state management.
However, a new contender had emerged, released by Facebook as the framework they use in production. It was called Relay, and it worked hand-in-hand with GraphQL, “a data query language and runtime.” Relay, though very young — it was only open-sourced in late 2015 — excelled at making data-fetching simple and declarative through the power of GraphQL.
So that was our task: to decide between Redux and Relay for our Flux framework. Let’s talk about each in a bit more detail.
The elegance of Redux
Redux is an astonishingly minimal, elegant, and powerful framework. It reduces the pain of having to pass props and actions down and up giant component hierarchies in complex applications and makes it easy to manage the internal state of your application.
Contrary to other Flux frameworks that maintain multiple stores, Redux has a singleton store that manages one hierarchical state tree. This adds a bit of programmatic complexity during setup, but is easier to understand and maintain.
In fact, you’d be shocked by how little code you have to write to get a lot done. And of course, Redux binds nicely to React component props such that changes in state trigger component re-renders. Gone are the days of manual bindings to event beacons.
So what’s GraphQL, anyway?
To be able to evaluate Relay, we needed to learn about GraphQL. Becoming familiar with the particulars of GraphQL is left as an exercise to the reader, but for our purposes you can think of it this way:
- It’s a query language that works with a schema you write to represent the data your front-end needs to present to the user.
- It acts as an abstraction layer between the way you store data on the back-end and the way you want to present them on the front-end. GraphQL enables you to write your schema such that the data you need to present are grouped together exactly as you need, regardless of how or where they’re stored.
- In order to use it, both your client and server need to speak GraphQL.
Where Relay fits in
Relay is the client side of that equation. It is a Flux framework that has a dispatcher and stores like Redux, but abstracts away the internals from the developer in a way that Redux doesn’t. Its responsibilities are twofold:
- Communicating with the GraphQL server to fetch and mutate data.
- Binding GraphQL queries to React component props. That’s an innocuous way of saying something powerful: Relay allows you to declaratively specify a component’s data needs, and it will take care of talking to the GraphQL server to fetch the data a component needs to render itself. This means your front-end developers don’t have to worry about the mechanics of talking to a server to fetch data, at all. That’s huge.
Relay vs Redux: final round. Fight!
Both frameworks have some compelling features, as well as drawbacks. Let’s make a list:
- Specify data needs declaratively
- Mechanics of data fetching taken care of by framework
- GraphQL lets you create data structures depending on presentational needs, thereby simplifying the front-end logic
- Very young, and its documentation is pretty bare-bones. Most examples available online are trivial. In short, the learning curve is steep.
- While it works very well to fetch and mutate data stored remotely, there is no built-in provision for managing non-persistent state.
- Very simple and elegant. Get a lot of benefit for a little code.
- Easy to learn and get going with. Good documentation and online resources.
- Managing shared application state is a breeze. Components listen to a part of the store seamlessly and predictably update based on changes to the state.
- No built-in provision for data fetching. That is left entirely up to the developer.
Though it took us a little while to come to this conclusion — we had originally decided on Relay alone — it became clear that Relay and Redux have complementary strengths, and instead of an either/or choice, it became both/and.
Today, we use Relay to take care of server communication and data fetching, and Redux to manage shared, non-persistent application state.
Next time: server-side frameworks.