Clean Architecture with reactive use cases

Piotr Zawadzki
the-stepstone-group-tech-blog
6 min readJun 6, 2018
“Two people in elegant shirts brainstorming over a sheet of paper near two laptops” by Helloquence on Unsplash

This article focuses on different types of use cases/interactors which we are using in our Android app. Use cases play an important role in Clean Architecture and I hope you’ll find our implementation useful!

TL;DR Show me the code!

A few notes before you start… As this is just a sample app some stuff is missing for simplicity e.g. handling screen rotations, avoiding code duplicates, having an actual DB connection/networking API. Please keep that in mind when browsing this code.

Enough with the excuses though…

What is Clean Architecture?

There’s a bunch of articles which will explain this way better than I ever could:

In a nutshell each class in Clean Architecture belongs to one of the layers below:

Source: https://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/

The main point of this architecture is that the business logic, also known as domain, is at the center.

Uncle Bob says it better though:

The Dependency Rule

The concentric circles represent different areas of software. In general, the further in you go, the higher level the software becomes. The outer circles are mechanisms. The inner circles are policies.

The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in an inner circle. That includes, functions, classes. variables, or any other named software entity.

By the same token, data formats used in an outer circle should not be used by an inner circle, especially if those formats are generate by a framework in an outer circle. We don’t want anything in an outer circle to impact the inner circles.

What are those use cases?

Also called interactors. Use cases are the classes from the business logic layer.

Quote from Uncle Bob:

The software in this layer contains application specific business rules. It encapsulates and implements all of the use cases of the system. These use cases orchestrate the flow of data to and from the entities, and direct those entities to use their enterprise wide business rules to achieve the goals of the use case.

We do not expect changes in this layer to affect the entities. We also do not expect this layer to be affected by changes to externalities such as the database, the UI, or any of the common frameworks. This layer is isolated from such concerns.

We do, however, expect that changes to the operation of the application will affect the use-cases and therefore the software in this layer. If the details of a use-case change, then some code in this layer will certainly be affected.

A use case can e.g. fetch data from multiple repositories and convert them to a specific format taking only the information needed.

Our implementation

Our implementation is based mostly on a sample app created by Fernando Cejas:

We share similar package naming and the concept is in general the same, however as we were migrating more and more stuff to Clean Architecture we found a number of ways to improve it.

As an image is worth a thousand words here’s a diagram showing different layers used:

Source: https://github.com/android10/Android-CleanArchitecture

And here’s a version with sample classes/interfaces showing how they are laid out over different layers (based on a screen showing some content loaded from DB/network):

What needed improving?

In the referenced sample app the communication between use cases, repositories and presenters is done via RxJava2. To be more precise — it is done via Observables. However, not everything should be an Observable in the reactive world of RxJava2.

Sometimes when communicating with repositories/services which were not returning an Observable but e.g. a Single or a Completable we had to convert them to Observables as use cases needed to return an Observable.

Aside from that, using Observables for everything can also be misleading. This happens since there is a difference between Observables, Singles and Completables (and Maybes) in what they are meant to do. There’s a great article where you can learn about it:

Moreover, sometimes we didn’t really need to have an asynchronous use case and handling that stuff on a separate thread seemed like an overkill.

Based on these differences we created separate implementations described below.

BaseReactiveUseCase

Nothing special going on here. It just contains the Schedulers to use for the created Observables/Singles/Completables and some methods for disposing a use case. This base class is used by all reactive use cases below.

SingleUseCase

Used for one-time operations e.g. API requests, reading data from database only once, etc.

When creating your own use case you need to implement the buildUseCaseSinglemethod. To execute it we need to pass a DisposableSingleObserverand optional parameters to the executemethod.

ObservableUseCase

To be used when we would like to be notified once things change e.g. when a table content update in DB with Room happens or when a broadcast is received with a BroadcastReceiver.

When creating our own use case we need to implement the buildUseCaseObservablemethod. To execute it we need to pass a DisposableObserverand optional parameters to the executemethod.

CompletableUseCase

To be used for asynchronous operations where we do not care about the result/response/data returned e.g. triggering a synchronisation, making a POST request where we do not care about the response.

When creating our own use case we need to implement the buildUseCaseCompletablemethod. To execute it we need to pass a DisposableCompletableObserverand optional parameters to the executemethod.

SynchronousUseCase

Not really reactive and does not need disposing but worth mentioning as not all use cases need to happen on a background thread. Can be used for reading data from SharedPreferences (which is quick) or data from XML resources, making calls to some 3rd party libraries which handle async operations on their own e.g. tracking APIs.

This is just a simple interface with a single execute method which needs to be implemented.

What about some samples?

You’ll find all the use cases described in this article in the repo. It might be a lot to take in if you’re not familiar with Clean Architecture, therefore I strongly recommend reading the articles posted at the top first.

Read more about the technologies we use or take an inside look at our organisation & processes. Interested in working at StepStone? Check out our careers page.

--

--

Piotr Zawadzki
the-stepstone-group-tech-blog

Principal Android Developer at Stepstone — passionate about technology, Android geek, photography enthusiast.