How to implement Redux in Kotlin Part 2: real world example

Liviu Coman
4 min readJul 14, 2019

--

This is the second article in a series about implementing the Redux pattern in Kotlin.

In the first part we built all of the main Redux entities that help power a simple counter app from the ground up.

That’s a really convenient way of getting into thinking about State, Reducers, unidirectional data flow, etc, but it’s not as useful as a real world example. With that I mean code that more closely resembles what we’d write on a day to day basis. That’s why in this post we’re going to look at how we can apply the Redux pattern to an app we’ve started in this article and continued in this one.

Feel free to first check those out, but as a reminder let’s see where we’re at.

This is the view we want to show on screen:

And this is all the code that we have so far, neatly decoupled:

How do we add Redux to the architecture we currently have?

The good news is that it’s pretty decoupled as it is. There’s bits we won’t need to touch at all, like the ViewStates and the redraw or binder methods. Those are things that pertain to the UI layer more than anything else, whilst Redux is squarely focused on the business layer. Which means that where we need to focus is on the Repository and the ViewModel. We can replace them with our Store.

Before we can do that, however, we need to start just as we did in the counter app example, by defining how our State and Actions will look like.

Building blocks

Let’s lay out our first State:

Our contact State is pretty simple and resembles the ContactDTO we defined as part of our Data layer. Indeed, one option would be to have the ContactState hold a reference to a ContactDTO directly. I don’t like this approach too much, since it gives our app’s State knowledge of data layer objects and thus couples it tightly together.

Going one step further, we can imagine our app holds a number of contacts, not just one. We want to hold them all in memory, so we can use a structure like a List, Set or a Map to defined all of the contacts in our app. That can be our global app state.

Finally, in a real world app, errors of different kinds do happen. It’s important we acknowledge that in our State and there’s various ways we can do that. One way is we can treat all errors as global events. We can show a popup or toast or any other type of error message when that happens. And we can define our ErrorState in relation to our AppState like this:

Now that we’re satisfied with our AppState, we can define our Actions.

Looking at the State and at what our contacts app needs to do, we can infer a number of actions:

  • we want to signal to the Store a new contact has been loaded
  • we want to signal we’ve added or removed a contact from favourites
  • and finally we want to signal if there’s been an error

Mutating state

With our State and Actions in place, just as before, we can write our reducers — the business logic units that will take the place of the ViewModel. In this case we have three reducers to write, one for each state: ContactState, ErrorState and AppState.

ErrorStateReducer is the simplest one so we’ll start with that. Generally, we want to return a new ErrorState any time the GeneralError action passes through and null otherwise.

ContactStateReducer is a bit trickier so let’s break it down action by action.

When the LoadedContact action passes through, we want to either add or replace a new entry in the contactState Map.

Conversely, when the ToggleFavourite action passes through, we want to check if a contact already exists in the Map, and if so, toggle the isFavourite flag to either true or false.

Finally we can get to the AppStateReducer:

This one’s a bit different than all other reducers we’ve written so far. It’s called a top-level reducer and it’s useful when you have a complex state tree. What it does is it always returns a new instance of the State and delegates creating each sub state to other individual reducers. This is a nice way of separating concerns.

Usage

With State, Actions and Reducers out of the way, we can focus on the last part — getting rid of ViewModels, Repositories, etc. To do that we have to go to our two Interactor implementations: LoadContactInteractor and HandleFavouriteContactInteractor. In both these cases we can replace ViewModel references to direct Store and Api references, like this:

Finally, we need to change our ContactComponent and remove the listener it had on the ViewModel with a StoreSubscriber:

And that’s basically it. Now we’ve further improved our initial decoupled architecture. We’ve grouped all our business logic into easily testable Reducers, we’ve made sense of all our app’s state by keeping a single copy of it in the Store and we’ve made reasoning about the entire system that much easier by applied a strict unidirectional data flow.

For reference, the final code we’ve come to is this:

This article is one of a multi-part series exploring State, UI and Redux and how we can leverage knowledge of these topics to write more modular and testable code.

--

--

Liviu Coman

Enthusiast of all things mobile and functional. Building a safer internet for kids as Engineering Lead @SuperAwesome.