Out of Angular Part 2 — Redux to the Rescue

This is a continuation of the story of 4 engineers figuring out how to evolve a dying platform. Part 1 here.

ES2015 had gone a long way in making quality of life changes to our workflow. We were still very aware that at some point in the future Angular 1 would cease to be maintained. We also knew that when that time came we wanted to be well on our way towards executing the exit plan, whatever that might be.

We had a problem with our existing app. We were generating too many needless API calls when we changed screens in our application. Our UI was in the driver’s seat and our data model was updated based on what the UI was doing.

If this sounds familiar then you’re not alone. Prior to the concepts introduced by data models like Flux and reactive 1-way data flow, many applications worked this way. Click a button fire off a promise chain snaking through the layers of your app to the API. As the data came back along that chain you were responsible for making sure all the various models in your system were updated correctly. It is clunky and error prone and as your system grows well intentioned models can pick up new functionality that they weren’t designed for. This could introduce fun side effects throughout the system and lead to a lot of whack-a-mole.

I had been spending more and more time with the React ecosystem and it was about this time that Redux was starting to make a splash in a big way. Here was a solution to a very real problems we had. One single state object. Immutability as a constraint removes unwanted side effects. All side effect code must happen outside in a container that then pushed into this single modification point.

This conceptual extension of the Flux model would allow us to put our data in the driver’s seat. Exactly what we wanted since a thermostat is a lot of state data that we mirror in our UI.

Getting back to the original problem, UI driving our API calls, we looked for something to make good sense of our polling logic that would feed the redux store. Redux-saga provided a way for us to break our polling logic into ES2015 generators and use async-await to step through each yield statement.

Our side effect API polling logic became very straight forward to implement.

We decided to hook redux in initially at the choke point of all our API calls called the connection manager. Our redux-saga was a polling daemon that worked independently of any UI, pushing changes into the redux store when they were detected.

Some of our promise chains still existed and plugged into the redux store / actions but as the migration process continued the redux store got bound closer and closer to the view layer as we replaced more Angular service logic with selectors built with reselect.

One of the choices we made when building the polling daemon was to use Fetch for all API calls. This proved to be the right course of action especially when you started to consider the greater React ecosystem as a whole. React-native React-native-windows all use Fetch as the bridge between native and javascript.

Now our data model was moving to a platform agnostic place. Redux can plug into everything. Since it’s API surface is so small building bindings for it in any framework is trivial.

migrating our low level data Angular services first got most of our common logic out of Angular and let us keep the same API surface / angular bindings of our common code. This meant that all our apps running our core libraries would benefit without having to do much retrofitting at all.

With the data model in place, the last choice we needed to make was around the view layer.

Our app was performing much better and was much easier to update. In part 3 we’ll dive into retrofitting react into an existing angular app. The challenge faced was making the two worlds work together.