2
\$\begingroup\$

I am implementing some of the architectural designs from Google I/O's app to my own app, but I have come across something in their app that has created some confusion for me.

They have a domain layer with repository usecases, which I use myself in my apps usually. However, I do have to provide these usecases with dagger in my app. But on Google's I/O app, I cannot find any module that provides these usecases. And when used with viewmodels annotated @HiltViewModel (In my own app), it seems like it works? Somehow these get injected into my viewmodels. I do have to provide all the usecase's dependencies(repositories etc) with Hilt, but I don't have to provide any usecase via Hilt.

Here is how my app architecture looks like:

enter image description here

Here is example how it looks in my code.

Usecase:

abstract class UseCase<in P, R>(private val coroutineDispatcher: CoroutineDispatcher) {

    suspend operator fun invoke(parameters: P): Resource<R> {
        return try {
            withContext(coroutineDispatcher) {
                execute(parameters).let {
                    Resource.Success(it)
                }
            }
        } catch (e: Exception) {
            Timber.d(e)
            Resource.Error(e.toString())
        }
    }

    @Throws(RuntimeException::class)
    protected abstract suspend fun execute(parameters: P): R
}

Concrete implementation of usecase:

class GetListUseCase @Inject constructor(
    private val coroutineDispatcher: CoroutineDispatcher,
    private val remoteRepository: RemoteRepository
): UseCase<ListRequest,ItemsList>(coroutineDispatcher) {

    override suspend fun execute(parameters: ListRequest): ItemsList{
        return remoteRepository.getList(parameters)
    }

}

Viewmodel:

@HiltViewModel
class DetailViewModel @Inject constructor(
    private val GetListUseCase: getListUseCase
): ViewModel() {

    suspend fun getList(): Resource<ItemsList> {
        getPokemonListUseCase.invoke(ListRequest(3))
    }

}

Example of provided repo:

@Singleton
@Provides
fun provideRemoteRepository(
    api: Api
): RemoteRepository = RemoteRepositoryImpl(api)

Remote repo:

@ActivityScoped
class RemoteRepositoryImpl @Inject constructor(
    private val api: Api
): RemoteRepository {

    override suspend fun getList(request: ListRequest): PokemonList {
        return api.getPokemonList(request.limit, request.offset)
    }

}

My Question is: Is this correct way of using Usecases? Should I also provide these with DI from a module as I do with all other dependencies in my project ? And why does this even work from the first place? How come I don't need to provide usecases with DI?

And should I have a single Repository class, which has remote datasource and local datasource as dependencies, or should I have a Repository class for each one? It is the same type of data that gets fetches from a remote source and then cached locally.

I can't find anything on the Google I/O app github repo where they have any kind of dependency injections where they provide usecases. I see that they are using delegates and delegatemodules to provide some of the dependencies to the viewmode, but nothing to provide usecases.

\$\endgroup\$

0