Hi all, I have a question, if `use cases` in `clean architecture` with Android are just calling `rep...
y
Hi all, I have a question, if
use cases
in
clean architecture
with Android are just calling
repositories
, why should we bother creating them? why not injecting
repositories
in
viewmodels
like we used to do?!
j
Personally I don't like repositories and I only use use cases
k
If your "usecase" is simple, you can have one or the other, but if logic is complex, having repository as single place to search for feature datasource API's and usecase as place where domain logic is done is a good separation imo.
y
can u give me an example of a complex use case (in šŸ¤–)?
k
I would consider it if it's talking more than 1 API that is in a lower level, this includes also if it's consuming 2 API's from same repository. Example, user changed some field, and we need to store data locally and send network request.
y
I see, but don't you think that in most cases we only go with passing the
input
and caring about
output
and most of the work is being done in the
repos
šŸ¤” ?
k
You can do it in repository, but for me, repository is the place where I put all my datasource API's, so it's a place of gathering the datasource API's, and not some high-level place that usecases were supposed to be.
y
Yeah as the name says it's a
repository
Thanks šŸ™
j
curiously, clean arch says nothing about repositories
k
I wonder if Gateways could be considered a Repository.
j
I think it is adding complexity which is going to be useless a lot of times, overall in Android applications, like creating mappers as objects that are injected lately instead of simple functions.
y
You're speaking my suffering man šŸ˜‚
j
almost all android application just draw fetching from API and adding offline support, without a really complex logic. You can inject all of those objects in the use case implementation and and don't have a huge function
meanwhile using both, repositories and use cases, you will get tons of anemic use cases
k
It should certainly be thought before if it makes sense.
c
Anemic use cases is a pain; but combining use cases and repositories is not the right solution according to me; and it is a layering violation. In the architectures that I've worked with: • UseCase resides in the domain layer • Respository interface resides in domain layer (so use cases have access to repository interfaces) • UseCases only handle the business logic. Specifically, UseCases do not know about where or how your data is stored. They definitely do not know about your backend URL or JSON formats. • Repository implementations reside in data layer. These repository implementations are the ones responsible for API calls/database calls/JSON parsing etc. A more viable alternative to avoid one-liner usecases is to inject repository interfaces directly into ViewModels where it makes sense, and inject UseCases in ViewModels only if the UseCase is more than a one-liner. However in practice I've found that it is makes things more predictable to always inject UseCases in ViewModels and never repositories (even if those usecases are likely to start off as one-liners). This is because many times a UseCase might start as a one-liner but then evolve to something more. As a concrete example in one project we introduced in-memory caching for some data; and then a bunch of UseCases which were one-liners turned into UseCases which had logic: First check the cache, if it is there return it; if not fetch it and store into cache before returning.
j
In my opinion just keep both in mind. Analyze the case you are implementing, when it is simple and needed in one place, there sometimes is no need to add an extra UseCase boilerplate. When code needed to use data from repository is getting complicated (business logics grows) and there is a need to use them in many places (depending on the rules in the project, for example: one, two, refactor), just refactor the code to UseCase to encapsulate shared logic.
f
A thing to keep in mind is that the presentation layer should not depend on the data layer. With use cases changes to the data layer are isolated to your use cases so that these changes do not leak to the presentation layer, forcing you to do a major refactor in your presentation layer if you make changes at the data layer. Data types should also not leak to the presentation layer, if you change backend technologies that should be transparent to the presentation layer.
k
Here is an interesting usecase, where you might see how big usecase can get. https://kotlinlang.slack.com/archives/C0B8M7BUY/p1661928688133889
j
You can have private function in an use case to allow to understand it.
Copy code
class SomeUseCaseImpl : SomeUseCase {
    operator fun invoke(id: Id): Flow<Some> {
      val someDto = fetch()
      someDto.saveInDatabase()
      return database.getSomeAsFlow()
    }

   // do network request 
   private fun fetch()

   // map to entity and save
   fun SomeDto.saveInDatabase()

   …
}
a
The approach suggested by https://betterprogramming.pub/how-to-avoid-use-cases-boilerplate-in-android-d0c9aa27ef27 seems to reduce the
UseCase
boilerplate while maintaining layering.
p
Test-ability mainly the reason.
d
The reason is simple, if you inject the repository instead, you add to your VM another reason to change ( remember that a class should have a single reason to change ). For example if before saving a user, you will need to check if the user is not restricted, you will need to change your VM, which is wrong. Additionally, you would need to track down wherever you're saving the user and apply the change
490 Views