Cleaner Flutter Vol. 5: Establishing Use Cases
Elian Ortega
Posted on January 31, 2021
Before starting: Previous Volume Cleaner Flutter Vol. 4: Hiring Repositories
Hello and welcome to the last volume where we will talk about the domain layer of the Clean Architecture proposal. Last time we talked about a fundamental part: the repositories, which allow us to create the communication of our software with the outside world.
On this occasion, with the use cases we are going to achieve independence from the specific interactions of the user with the system, using use cases and thus obtain maintainable, reusable code that truly meets the needs of the business.
But before we get into technical matters, it is important that we remember that there are 2 perspectives: a developer's and a business perspective of the product. This differentiation is a very important topic, since by being clear about the objectives of the product we can develop software in a better way.
Remember that all software products should first go through a process where their requirements and the interactions of a user with the software product are defined, whether this is a login
,add an object to a shopping cart
, or process a payment
.
Ok, let's get into it.
What are use cases?
In uncle Bobs words ...
The software in this layer contains the application-specific business rules. These encapsulate and implement all the use cases of the system.
I remind you that the use cases mentioned in the definition do not refer coding part or module, but to the use cases that came from de planning phase of the project.
Each event is an interaction of the user with the system and we can call this a use case. As this is also a buisness concept the most common way of the displaying the use cases of the system is by the use case graph, that looks like this:
From: https://warren2lynch.medium.com/use-case-tutorial-for-dummies-8cf426043710
In this we have objects like:
- Actor: Users who interact with the system.(might be another software too)
- Use case: How the actor uses the system to fulfill some functionality.
- Relationships: The relationship between the actors and the use cases
The example shows some use cases for a passenger at an airport. Which, as I mentioned before, is the user's interactions with the system, in this case the logic of an airport.
If you want to read more into these diagrams and their components, here is an article for you*: Use cases for dummies.*
In the code…
In my experience, I have noticed that the use case layer is usually bypassed and replaced using methods directly in the software's business logic. But when we implement it we achieve better decoupling and other advantages.
By adding the use cases layer the folder structure for the domain layer would look like this:
The principles (SOLID)...
Simpler repositories. When we create the use cases, we manage to facilitate communication with the repositories and we make sure to have better abstractions that meet the specific objective of that use case, and as we saw in previous articles, with this we comply with the single responsibility principle.
We use interfaces, not implementations. In use cases we are normally going to depend on a repository, which rather must be an interface,this way we would be complying with the Liskov substitution principle, since we could replace the repository with any other that implements the same interface and this will not affect the base logic of our use case.
Implementation in Dart
Like any of the components we've talked about throughout the series, each can be implemented in many ways. This time with the use cases we are going to use one of Dart's not very well known features.
In Dart there is something called callable classes. This allows us to use the instance of a class as if it were a function. To achieve this, all we have to do is implement the call()
method inside the class.
We get something like this:
Using this Dart feature, we can create a class for each of our use cases and implement the call method with the respective functionality.
Let's put the pieces together
With all the information we have, we can already see an example with all the pieces that we have carried so far: entities, repositories and use cases.
The code is simplified to facilitate the example. I also remind you that in order to understand the examples it is important that you have read the previous articles. I leave the previous one: Vol 4: Contratando Repositorios.
Let's imagine a very simple case of a signIn
, for this we are going to have a user entity and a repository interface that contains a method to make the signIn
using an email and password.
This is where many developers decide not to implement the use case layer, so they call the repositories directly from the application's business logic.
But since we are using use cases, we create a SignInUser
class which will be our use case. This will have a dependency on the AuthRepositoryInterface
, then we implement thecall ()
method and callsignIn ()
. And so we have a use case with sole responsibility and decouple the repositories from the business logic (state handler).
Note that it does not matter who wins the endless battle of which is the best state manager, since it does not matter which one we select, or if we even decide to change, this does not affect our implementation at all.
This example is quite simple, but for the moment what we are trying to do is understand each of the components that are part of this Clean Architecture proposal. Later we will make examples and videos applying all the theory with production code.
As always ...
If you learned something new, remember to leave your feedback and share with your fellow developers friends so that we continue to improve as a community and develop quality applications using Flutter.
For any questions and more content you can contact me through my social media:
Posted on January 31, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.