Open kryanod opened 8 years ago
In example 1 : i'd say that pagination is view logic, and that there is one UseCase that is able to call the repository and has arguments to get data from the pagination logic in the View Layer to return the correct results.
In example 2 : If you have logic where you execute one UseCase and depending on the results execute another UseCase, then you have'll have to create one UseCase to handle that decision making. And probably abstract some logic as well, because it will be used for multiple UseCases.
Example ( a bit of a dumb one, but just to describe the scenario) : UseCase 1 > Login User via Api UseCase 2 > Get the Search results for that User
The problem is that is UseCase 1 is successful, we need to execute UseCase 2, if not, there's no need for that. The solution is not to call those 2 UseCases but create UseCase 3 (login user and if ok, get the search results). It's not because you already have atomic UseCases for certain actions, that you cannot create a new one to handle a specific business task. Now depending on the complexity, you might abstract things like "Login User via Api" to a separate "Api Manager" which both UseCase 1 and 3 may call, so there isn't any specific duplicate logic in both Use Cases.
Hey @Trikke and @AlexTimonin !
Please I could lend some advice on this pagination/use case scenario.
Are you saying that the view should know about which is the reference to the next page? Or should it be the responsibility of the presenter? (That is orchestrating everything?)
So is the flow something like this?
I am a bit unsure if this is the way to do this.
Thank you.
Hey @fabiocarballo ,
It should be the responsibility of the Presenter. The only thing the view does is handle the event for when the next page should be loaded. The Presenter is then notified and handles the logic with pagination details.
So :
Did this help?
@Trikke Very clear, however can you outline exactly the difference and responsibilities of the user case vs the repository?
@trungie the responsibility of the repository is store and retrieve data that is used in the Domain layer. How it does that doesn't matter to the Domain layer. So it is an abstraction between the actual implementation of the store (sql, plain text,...) and the business part of your app.
A Use Case is part of this business bit of your app. You can have a lot of those, and sometimes even for the smallest, most idiotic things. Your business rules (read how your app works) reside in the Domain layer and usually in these Use Cases. Lets make a quick, dumbed down, example:
We create an app to get the top 5 GitHub projects of today (this sentence is our actual business logic). So we have a screen with a button "get projects". That's the only thing our app will do, for now. The user presses that button, the View notifies the Presenter that the button is pressed and it needs to do something. The Presenter will then have or create a Use Case. This Use Case's only purpose is to enforce our business rule from above, getting the top 5 GitHub projects of today. In the Data layer, we have a Repository that is able to fetch and store the GitHub projects. The Repository will also have parameters like "amountOfProjectsToFetch" and "projectTimeFilter". The first one is to specify the amount of projects to retrieve from the Github API, and the second one exist to indicate a timeframe to filter these projects. (reminder, i'm making this up as we go along, the Github API in reality will work differently) Now back to our Use Case. It will create an instance of the Repository and will call the method to retrieve the projects with the parameters "5" and "TODAY". So the Presenter calls the Use Case and it will run, returning the top 5 GitHub projects for today. The Presenter will then re-render the View and show these.
The idea to take away here is that it isn't the Presenter or the Repository that gets to specify how many projects we need to get from Github. There isn't such a method in the Repository, it only gets lists of projects from the GitHub API with parameters. It doesn't case that our app wants the top 5 GitHub projects. The other way around, the Use Case doesn't care HOW we get these top 5 GitHub projects. It only cares that there is a way to do it, and that it can dictate WHAT to get.
Now imagine you want to expand your app and show a button to get the top 10 GitHub projects of this year. You only need to add a new Use Case that does this, and have your Presenter execute that one on the right button press.
Now imagine this last feature is a paying feature. This is business logic. So we modify the last Use Case to also check if the current User has payed for this feature, and block access to it if needed. It would either return the top 10 GitHub projects or notify the Presenter that the user cannot, and should see a payment dialog.
(keep in mind, this is all very simple and might seem like a lot of code for a small app. But soon your app grows and grows, and you keep adding new features and rules. Then this architecture will start making more and more sense as testability and usability remain simple)
@Trikke Thanks for making great explanation.
Now imagine this last feature is a paying feature. This is business logic. So we modify the last Use Case to also check if the current User has payed for this feature, and block access to it if needed. It would either return the top 10 GitHub projects or notify the Presenter that the user cannot, and should see a payment dialog.
I still have some questions about sentence above.
@rshah
A quick example :
Requirement of App
The user can get a list of Top 10 Github Projects, but he has to have the paying status of "Premium Member" in your app. If the user is not, he must be shown a payment dialog.
So the control flow would be
Please read up some more on programming patterns. The ones shown here in this sample are not the only ones, and will not cover all your requirements. So don't treat this as "this is the final example on how to do it, and i only will need this". There's a lot more out there, explore!
Hi @Trikke,
I have two UseCases: ResetUserPassword:
Both UseCases share the same first part "GetUserIdFromPreferences -> QueryUserFromDB".
I'm trying to apply the same concept in my app (have a common place for "GetUserIdFromPreferences -> QueryUserFromDB") to avoid repeating myself.
Is it correct to have something like a common method:
Observable<User> getLoggedInUser(PreferenceRepository preferenceRepository, final UserRepository userRepository) {}
placed in a new class
public class UserManager {}
Or, there's a better method to implement?
What can subclasses of UseCase do? What can't they do? In this project use cases are atomic tasks used to simply obtain data, but what about decision making, where does that belong? Example 1: I have a collection of items and lazy loading. Is there one UseCase handling all pagination logic and calling necessary repository methods, or are there multiple UseCases and something (presenter?) orchestrating them? Example 2: after executing a UseCase, based on its response we make a decision on what UseCase to call next. Again, where is this decision made?