Why you need Use Cases/Interactors

Denis Brandi
ProAndroidDev
Published in
7 min readOct 16, 2019

--

Since Clean Architecture became the new hot topic in the Android world there have been loads of code samples and articles that tried to explain how it works and which are its benefits.

However, I always hear these questions from developers that try to apply it:

What are these Use Cases/interactors supposed to do?

Why can’t I call directly the repository from the Presenter/ViewModel?

Why should I have a Use Case that does nothing other than just calling a Repository, isn’t this an overkill for my app?

Do I really need a Use Case for each repository method?

These are all valid questions and the aim of this article is to give some clarifications.

I hope I won’t fail miserably.

Layered Architectures and the Application Layer

Layered Architecture

In order to understand Use Cases you first need to understand Layered Architectures and the Application Layer (also known as Service Layer).

Separation of concerns

One of the primary goals of any architecture is the separation of concerns.

There are different ways a software system can be divided but after years of experimentations the industry converged on using few standard layers:

  1. Presentation: Responsible for showing information to the user and interpreting user commands;
  2. Application: Defines the jobs the software is supposed to do by orchestrating the data flow from and to the domain models;
  3. Domain: Represents concepts of the business, information about the current situation and business rules;
  4. Data: Responsible for persisting domain models.

Layers implementation

Layers can be implemented in several ways, based on the size of the program you can choose among different approaches:

  • if you are writing a simple bash script you can just split your main procedure into different subroutines.
  • if you are writing a simple Android Studio plugin you can separate your layers in different classes.
  • if you are writing a simple App you can separate layers using packages.
  • if you are writing a complex App or Webservice you can separate layers using modules.

I’m pretty sure you are used to seeing at least 3 main broad packages (or modules) in most of the Clean Architecture examples: Presentation, Domain, and Data.
This is due to the common 3-tier PresentationDomainDataLayering approach, in which the Application Layer lies inside the domain package.

Now I would expect this question from you:

Why are Domain and Application together if they are different layers?

The 2 “business logics”

Domain and Application layers both represent business logic, however, their nature is of 2 kinds:

  1. Domain business logic: here you find the “models” of your app, which can be of different types (Aggregate Roots, Entities, Value Objects) and that implement enterprise-wide business rules (they not only contain data but also processes).
  2. Application business logic: here you find the so-called “services”/“usecases”/“interactors”, situated on top of models and the “ports” for the Data Layer (used for dependency inversion, usually Repository interfaces), they retrieve and store domain models by using either repositories or other Use Cases.
    If you want to know more about Repositories I invite you to read my previous post.

In PresentationDomainDataLayering each package represents a different topic: UI logic, business logic, and persistence logic.
Because Domain and Application cover the same topic and since the Application layer is “thin” they are often grouped together, however, there is still a separation at the class level and/or at a sub-package level:

Example of PresentationDomainDataLayering package structure

Avoid “God” Presenters/ViewModels

What happens when we don’t separate properly the concerns? The answer is “we have god objects”.

Let’s have a look at a ViewModel without Use Cases vs a ViewModel with Use Cases.

Without Use Cases

The ViewModel:

ViewModel without Use Cases

The dependencies:

Dependencies

With Use Cases

Let’s now add the Use Cases:

Use Cases

All the previous dependencies stay as before with the only difference that they are now used by the Use Cases instead of the ViewModels.
And now the ViewModel:

ViewModel with Use Cases

At first look, you should notice that thanks to Use Cases our ViewModel is much leaner since we get rid of the dataflow logic.

You should also be able to see how the first approach without Use Cases doesn’t scale very well: if our TransactionRepository now needs an extra parameter coming from another repository our TransactionsViewModel will need to use an additional repository and maybe handle an extra success/failure logic as well.

And at last reusability: thanks to those Use Cases if you have another ViewModel that uses the same dataflow logic you can reuse the same Use Cases.

“Useless Use Cases”

Many times while writing Use Cases you end up in the following situation:

class SomeUseCaseImpl(
private val someRepository: SomeRepository
): SomeUseCase {
override fun invoke() = someRepository.getSomething()}

I know this situation very well and I also know this case is more common than the cases in which the Use Cases are actually doing something!

However, even if they “do nothing” other than just calling a repository method, there are several reasons for still using them.

First: Consistency

Wouldn’t it be bad if some ViewModels would call use-cases while others would directly call repositories?
Some less experienced developers or new joiners will have a hard time understanding what they are supposed to do while looking at the codebase.

Second: They protect the code from future changes

One of the purposes of Clean Architecture is to give you a codebase that can easily adapt when requirements change, which also means that the amount of code that needs to change should always be minimum.

Have a look at this case:

interface SomeRepository {
fun doSomething()
}
class SomeViewModel1(private val someRepository: SomeRepository) {
//...
fun doSomething() {
//...
someRepository.doSomething()
//...
}
//...
}
class SomeViewModel2(private val someRepository: SomeRepository) {
//...
fun doSomething() {
//...
someRepository.doSomething()
//...
}
//...
}
class SomeViewModel3(private val someRepository: SomeRepository) {
//...
fun doSomething() {
//...
someRepository.doSomething()
//...
}
//...
}

As you can see the SomeRepository is used in multiple places.

If the requirements for the repository change, let’s say the doSomething method now needs a model coming from a SomeOtherRepository, how much code do you need to change?
How many ViewModels are affected?

If you had a Use Case in the first place you would now need to update only your Use Case implementation.

Third, The “Screaming Architecture”

What is your app doing?

I guess you have no documentation that explains what your app does.

Don’t get me wrong, I’m not expecting you to have one, however, if I want to know what your app does where should I look at?

The answer is the code, the code is the only source of truth.

When you open your project can you tell what your app does?

In most of the projects that you’ll see you won’t be able to understand what the app is about at first look.

This might not bother you since maybe you started the project from scratch, but what about new joiners? How long will it take to them to understand what they have to do and how?

Wouldn’t it be easier if the project structure would be something like this?

Use cases of a Banking app

Can you say what this app is doing by just looking at the project?
What is the project structure “screaming”?

It’s a simple banking app. And what can you do?

  • Create an account and log in
  • Request a new card or freeze a current one
  • Make payments and create standing orders
  • Get all the transactions the user made

Now yes, I cheated for the sake of the example, in a real project you would have much more noise than just Use Cases but still, you should be able to understand what the app does at first look.

Update: “Useless Use Cases” are no longer a problem if you use the Functional Interfaces approach as shown in this article.

A Use Case per Repository method?

This is a very common question and the answer is: most probably yes.

Well, the answer may look too simplistic to you. But the problem is not the answer, is the question.

You don’t have a Use Case because you have a repository method, you have a repository method because you have a Use Case that needs it.

Clean Architecture is a Use Case driven architecture, hence each repository method exists only because it is supporting a Use Case.

It is also true that a Repository method should be used by one, and only one, Use Case so that if we get rid of a Use Case of the app then the persistence logic that is supporting it should disappear as well.

Now, I said you’ll “most probably” have a Use Case for each repository method, the reason why is not always true is that a Use Case can use more than one Repository and more than one method (as far as it is the only Use Case using them).

Summary

Let’s now review the initial questions and let’s give them an answer.

Q: What are these Use Cases/interactors supposed to do?

A: They implement the dataflow logic.

Q: Why can’t I call directly the repository from the Presenter/ViewModel?

A: Because we want to avoid God objects that deals with both presentation logic and dataflow logic. We also want the dataflow logic to be reusable across different ViewModels.

Q: Why should I have a Use Case that does nothing other than just calling a Repository, isn’t this an overkill for my app?

A: Because of consistency, protection from future changes and for having a “Screaming Architecture”

Q: Do I really need a Use Case for each repository method?

A: Not always, but for sure you need at least a repository method for each Use Case.

If you managed to read the whole post without falling half-asleep I hope that you now have a better understanding of Use Cases.

--

--