repo_case
is a new package on pub.dev that allows you to auto-generate usecase classes from your repository signature class. Usecase classes are based on the Clean Architecture from Robert Martin (Uncle Bob). The project is inspired by the Flutter TDD Clean Architecture Course from Reso Coder.
What exactly do we need usecase classes? What are repositories? Why would I even want to use this package? Let's read on.
Project organization
An app does not need to be properly organized to work. It is easy to just write the code required to have a functional app in no time. What is the problem then? Why even consider an 'Architecture'?
You should try. As soon as your app starts to grow, things start to go wrong, really wrong.
Suddenly, your app does not work anymore in some cases. You cannot really debug (or test) these cases because you cannot find the source of the problem. Is it at line 30 or line 1024? Maybe in more than one file?
Welcome the Clean Architecture!
Our goal is to organize a precise structure that allows us to give each file a responsibility. When something breaks, you will have clear tests and logging telling you exactly what has gone wrong and where.
https://www.youtube.com/watch?v=KjE2IDphA_U&list=PLB6lc7nQ1n4iYGE_khpXRdJkJEp9WOech
Reso Coder's course about TDD and Clean Architecture on YouTube. It is a gamechanger!
I highly encourage you to follow the course to learn more about it. It will really change how you program, guaranteed!
Repository
For the purpose of this tutorial, we will focus on repositories and usecases.
What is a repository? A repository is a class responsible to fetch some data from 'the outside' (Database, API, local storage), convert it into proper objects, and pass these objects to 'the inside' (Views
, Widgets
, Pages
).
Think of it like a post office.
Your Users come to your House (app), you manage the app by calling the Post Office's usecases, which will then fetch what you requested from the outside database.
- You need a package delivered to your house (
Widget
). - You go to the office (
Repository
) and ask for it. - The post office will exactly tell you what will you receive (
Object
from outside) and what information it needs from you to process the delivery (Parameters of the method in the repository).
Done! You simply provide the parameters, and the repository will return you back what you asked. You (Widget
) do not need to know how it happened. You simply get and consume the answer!
Usecases
What is a usecase then? A usecase is simply the list of operations that the post office can perform (the methods of the repository class).
The post office (Repository
) may be specialized in delivering Login parameters. It is called PostOfficeLoginRepository
. When you go there, you are presented with a brochure that tells you the operation you can request in this office:
/// Hi customer, here are what we can do for you!
abstract class PostOfficeLoginRepository {
Future<String> getYourUsername(String email, String password);
}
Well, this post office looks poor. The only operation it can do for you is getting your username.
Okay then. You are asked to compile a form with your email and password. Once you are done, you will get back (sometime in the Future
) a String
with your username (or probably some error or something if you gave them the wrong parameters).
Okay, how do I code this?
Great question, let's write some code.
The app will be structured in three layers:
Presentation
(Your home)Domain
(The post office)Data
(The 'outside')
Your home is what the users of your app will see. The views, screens, widgets, UI, UX etc.
You will be responsible to manage (State management!) the data in your home. In order to do that, you will ask the post office for what you need.
The post office will then fetch what you request from the outside, convert it, and give all back to you.
Specifically, the list of things you can ask to the post office are our usecases.
Why do I need repo_case?
We are the post office now (actually you, as a programmer, are the major of the city, you must manage and create everything).
We want to define which operation we can perform. We use a brochure (a class) for that.
This brochure (class
) is a signature (i.e. is abstract
) of all the operations available, what they return, and what parameters do we need to perform them. Here you have it:
abstract class PostOfficeLoginRepository {
Future<String> getYourUsername(String email, String password);
Future<User> getUserFromUsername(String username);
Future<bool> postMyNewUsername(String username);
}
We will eventually need also to define a concrete implementation of our post office (not in the scope of this tutorial).
What are we going to present to our client?
Every one of these three methods we defined (getYourUsername
, getUserFromUsername
, postMyNewUsername
) will have its own usecase class.
In this way, you do not need to have a complete reference to the post office (repository
) when you only need your username.
You will depend only on one usecase (another class), called GetYourUsername
, that will tell you exactly the parameters it needs and it will talk with the post office to get it.
Here is how it is defined:
class GetYourUsername {
final PostOfficeLoginRepository postOfficeLoginRepository;
const GetYourUsername({
this.postOfficeLoginRepository,
});
Future<bool> call(GetYourUsernameParams params) async {
return postOfficeLoginRepository.getYourUsername(params.email, params.password);
}
}
class GetYourUsernameParams {
final String email;
final String password;
const GetYourUsernameParams({
@required this.email,
@required this.password,
});
}
repo_case???
Why repo_case
?
Well, look at the code above. It seems complicated (it isn't really). It looks repetitive!
You will need to define two classes like that for each of your methods in the repository!
What? The brochure looks so clean and simple, why do I need to spend my time managing also all these classes? Well, fear not, you do not need to!
Welcome repo_case
!
What repo_case does for you is saving your time. You do not need to write usecases anymore. Simply do this:
@repoCase // <- repo_case in action!
abstract class PostOfficeLoginRepository {
Future<String> getYourUsername(String email, String password);
Future<User> getUserFromUsername(String username);
Future<bool> postMyNewUsername(String username);
}
Done! Complete! You can forget about usecase classes (implementation) from now on!
The package will auto-generate all the usecase classes you need based on the signature of your repository.
Here you have it.
For more technical details, check out the documentation on pub.dev.
Like the package if you like it (Yeah!).
Follow me on Twitter at @SandroMaglione. Or check out more on my website (this one you are in).