In the first part of this blog post series, we saw the basics of what an unidirectional data flow architecture is. We learnt the main components used in our approach, their responsibilities and how they interact together.

In this second part, we are going to see some code, we are going to go through each component showing code snippets of their structure and main responsibilities. The code is based on the sample app, KUnidrectional, which is a simple app that follows the unidirectional data flow approach.

Ok, let’s go! 😄

View & ControllerView

View

As we saw previously, the View is our UI, the component that the user interacts with. For us, in Android, Activities, and Fragments are our views.

As we saw before too, each View has an associated ControllerView.

The ControllerView will forward the information to its associated View as soon as it receives a new state.

We could have Activities, Fragments, or both, to simplify, we are just going to use Activities. Following the sample app, we will have two Activities, one with a list of items, and another where we can create/edit an item:

Our two Views (two Activities). One with an list of items, the other where we can create/edit an item.

Basically, we have to do three main things in each View:

Setup its associated ControllerView.

Invoke the ControllerView when the user interacts with the View, so that we can start the unidirectional process.

Handle the new information the ControllerView will provide to its View, so then the View can render itself with the new information.

We can see everything we have talked about above in the following code snippet:

ControllerView setup and usage, we are defining an interface, used as a way to communicate back from the ControllerView to the View. For more info, have a look at the class on GitHub.

ControllerView

We know already how the View sets up its ControllerView, how it uses it, and how it implements an interface used for its ControllerView to communicate back.

As we saw when we configured it, we had a set of parameters, these parameters were:

ViewCallback : the interface that the View implements, so the ControllerView can communicate back to its View when it receives a new State .

: the interface that the View implements, so the ControllerView can communicate back to its View when it receives a new . Store : our core, it will be injected, so the ControllerView can dispatch Actions to the store, and subscribe to it, to receive new states.

: our core, it will be injected, so the ControllerView can dispatch to the store, and subscribe to it, to receive new states. Thread: where we want to run our code when we receive a new State. When a specific thread has not been injected, the ControllerView will not change threads when receiving a new state, so it will use the same where it’s running.

This is very useful as well when running integration tests, so we can make that our whole unidirectional flow runs in the same thread so it can be easily tested.

We can see now how a ControllerView looks in the following code snippet:

ControllerView that dispatches some actions and handles state. For more info have a look at the class on GitHub.

The ControllerView have some parameters injected through its constructor, it will use them, and will make our lives easier while unit testing this component since we will be able to mock them.

We see as well some functions that we have, like fetchItems, toEditItemScreen, and deleteItem. These are the functions that will dispatch actions to the Store. These functions are invoked by the View.

We override handleState function adding the specific code that we need to handle the content of the state, and then, once that we know what we should do, we’ll invoke the functions the View implemented (ViewCallback interface) so the View can do what it must do.

Everything sounds familiar to you, right? yes, what we have just seen is a typical MVP pattern where View → View and Presenter → ControllerView. Nothing fancy, nothing new. 😉

Action

As we saw in the previous article, Actions describe what we want to do, what we want to achieve. Actions are triggered from the ControllerViews and from SideEffects, and are handled in the Store by its Reducer.

In Kotlin we have data classes, that are pretty convenient since they are final classes and have default equals/hashCode/toString and copy functionality. Using val parameters in our Action classes, together with copy function, will provide the immutability we want.

We take advantage of it, so we implement our Actions as data classes, what it means that once that we create an Action, this action will never change.

We also have sealed classes that will help us to create an action-hierarchy, where we can group actions that we think are related between them, for a better comprehension, and later, when we work with our Reducer, for a better code maintainability.

We can see here the Actions hierarchy where we have groups of Actions depending on their purpose. For more info, have a look at the class on GitHub.

Store

As we saw in the previous article, the Store is the core of our architecture, it’s where our business logic resides. It will receive Actions and will dispatch new States.

Our Store basically has to:

Allow ControllerViews to subscribe to it, so then it can dispatch new states to them.

to to it, so then it can new to them. Allow SideEffects to subscribe to it, so then it can dispatch new actions to them.

to to it, so then it can new to them. Allow ControllerView and SideEffect to dispatch actions to it.

Reduce an action given the current state.

an action given the current state. Dispatch a new state.

Let’s see how the Store looks in the following code snippet:

Here you can see the main functionality of our Store. For more info, have a look at the class on GitHub.

Above we can see how our basic Store has:

A list of Actions . A blocking queue so we can process Actions in a FIFO manner.

. A blocking queue so we can process Actions in a FIFO manner. A list of SideEffects . A thread-safe list where SideEffects can subscribe to.

. A thread-safe list where SideEffects can subscribe to. A list of StateHandlers . A thread-safe list where ControllerViews can subscribe to.

. A thread-safe list where ControllerViews can subscribe to. The current State . Every time we have a new State, this will be updated.

. Every time we have a new State, this will be updated. Optional, a thread that is passed by parameter when the Store is configured and that will be used, in the case of any, to run the Store functionality.

As we saw in the ControllerView section, the way they send actions to the Store is just invoking a function, this function is dispatch(action) ,

and will be used as well by SideEffects when they have actions that they have to send to the Store, as a result of a previous handled action.

We could have done this in other ways, like using an event bus library where the Store is listening to incoming Actions, and ControllerViews are listening to new States; or using RxJava where ControllerView are Observers and the Store is an Observable that given Actions emits States, but for simplicity, as I said at the beginning, I didn’t want to use external libraries, neither for dependency injection, nor for this, so we could just focus on what’s important here avoiding other complex “problems” these approaches could give us too.

Once that we have the action the ControllerView (or the SideEffect, remember, SideEffects can dispatch actions too) has dispatched and needs to be processed, we are going to use these functions:

private fun handle(action: Action) {

val newState = reduce(action, state)

dispatch(newState)

sideEffects.dispatch(action)

}



private fun dispatch(state: State) {

this.state = state

stateHandlers.dispatch(state)

}

First, what we do is to use our Reducer. Given the current action and the current state, after we run our reduce function, we will have a new state, then we will update the current state with the new one and we’ll dispatch the new state to the state handlers (ControllerViews).

Once that we have done that, so the state has been updated, we will dispatch the current action to SideEffects, so they can handle it.

As you see what the Store does is pretty simple. 😄

By the way, probably you are wondering about the dispatch function that stateHandlers (aka the list of ControllerViews) and SideEffects have and use.

Since both are very similar I created a Subscribers abstract class that they extend, so they can change the thread that are going to use, and an extension function that basically iterates over each item on the list invoking the function that will really dispatch the action or the state:

fun <T> CopyOnWriteArrayList<out Subscriber<T>>.dispatch(data: T) {

forEach { it.onNext(data) }

}

Reducer

Ok, we have talked many times about the reducer but we haven’t seen how it looks like. It’s also pretty simple:

Given an action and the current state, we apply a specific reduce function that will generate a new state. For more info (the whole Store class), have a look at the class on GitHub.

Here, in the reducer, is the only place from our whole code base where we change the state, so it’s pretty easy and useful to know that here and only here the state of our app is where is going to be changed.

As you can remember from the actions section, actions are grouped together taking into account their responsibility using sealed classes, this gives us a nice hierarchy and division between action purposes, so we have actions that create items, actions that update items, etc.

If we divide the reducer functionality into small reducers, it makes its usage very easy, so we have a reduce function that is going to handle just creation actions, another that is just for update actions, and so on.

Need to add more actions? you just simply have to add the code to its own small reducer.

Adding a new group of actions? You just have to add the new group here and a new “reducer group function”.

Some of you will say that here we are breaking open/closed principle, since our store, in this reducer function, is open for modifications, so every time we create a new set of actions we have to handle them here.

That’s true, but the change is minimum and centralized.

You could, definitely, try to handle the reducer in a different way to avoid breaking open/closed.

An alternative, for instance, could be that actions and states can themselves being reduced together like:

data class CreateItemAction (val item : Item) : CreationAction() {

fun reduce (state: State) : State {

//TODO return new state

}

}

In the store-reduce function, we would just have to invoke the reduce-action function passing the state.

private fun reduce(action: Action, currentState: State): State {

val newState = action.reduce(state)

return newState

}

This alternative has the handicap that actions would have knowledge of the state, and I don’t really like that (even though the reducer looks very clean) .

But hey! feel free to adjust it to your own taste/preference, probably, there are even better options, let me know if you have one! 😉

State

The state is the representation of our core business data, and we haven’t shown how it is yet, so here we go:

Here you can see our basic State that is used in the sample app looks. Have a look at its code on GitHub.

As you see here, our State is based on different data classes that have just val parameters so it makes our state immutable.

You can see how we have divided our state into three different fields:

Navigation, it says to where we want to go.

Items list screen, it has an items collection.

Edit item screen, where we have the current item we are modifying/creating.

This is just an implementation detail of how our sample state it is, but now you can have a basic idea of how a state looks.

Side Effect

We said that SideEffects are optional, we can have them or not, but actually, side effects are the ones who enrich and add extra functionality to our core, side effects add persistence, side effects add network layer, etc.

Side effects look pretty similar to the Store, they are listening to incoming actions, once they get one, they will do their stuff, and then, optionally, they can dispatch a new action to the store as a response of what they have done.

Here you can see how a side effect looks like. For more info have a look at the class in GitHub.

As you can see above, our persistence side effect is pretty simple:

Subscribes to the store, so it can listen to actions.

to the store, so it can listen to actions. Handles an incoming action.

an incoming action. Optional, it can dispatch a new action, after the incoming action has been handled.

As you can see the handle function looks pretty similar to the reduce function in the store, but since here we are not reducing the state, I’ve changed its name.

The same that happens with the reduce function, this “handle” function hides implementation details about how the persistence layer works, here we don’t know if is an E-R database, or an OOO database, we don’t know how an action that wants to update an item works under the hood, we encapsulate everything in the different and independent from each other handle functions.

Dividing functionality into small and independent components makes really easy to eventually change the whole persistence mechanism without having to deal with huge refactors, persistence layer functionality lives here and just here, and inside here, decoupled from the main handle function.

Threading

As you know, in Android we have the UI thread (aka main thread), that is used by the system for updating the UI, if we block this thread doing long time operations like IO, we will give to our users a bad experience and our app will probably throw an ANR.

We don’t want that, we want to run as smooth as we can, and doing everything right.

As you remember, we have the View that is the element that the user interacts with (Activities and Fragments), we have the Store, and we have SideEffects.

What we are going to do is:

View will run in the UI thread (of course), it will use the ControllerView to dispatch Actions. ControllerViews are also running in the UI thread.

will run in the UI thread (of course), it will use the ControllerView to dispatch Actions. ControllerViews are also running in the UI thread. Store will run in its own thread, so every time the ControllerView invokes the dispatch action, we are going to change the thread we were using, so what we do in the Store will be done in a separated thread from the UI.

will run in its own thread, so every time the ControllerView invokes the dispatch action, we are going to change the thread we were using, so what we do in the Store will be done in a separated thread from the UI. SideEffects will run in their own thread, every SideEffect will run independently since we don’t want that the execution of one SideEffect could block what other SideEffect is doing. Every time we dispatch an action from the Store to the SideEffects we will change the thread so we will go from Store thread to the specific SideEffect thread.

Once that we have a new state, we will dispatch it to the ControllerView and will change the thread. We were in the Store thread and we will move to the UI thread, then when the ControllerView gives the new information to the View, the View is already running in the correct thread.

In this image you can see how the three main different components run in their own threads.

As you may remember, before we saw how the threads were passed as parameters in the ControllerView, the Store, and the SideEffect.

Each component is going to check then if it has a default thread that has been passed in its constructor. If the component doesn’t have a custom thread, it will run in the same thread that has been invoked, if it has one, it will change the thread that is going to use.

In Kotlin, we have default values for parameters so it’s very handy that we can define threads in our production code and in our test code we simply don’t do it, having null as a default thread value.

Having the option to define our threads, it makes really easy to create integration tests where all the pieces run in the same thread if no threads were defined, but in “production”, all components run in different ones.