Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can Hilt be used effectively in multi-module apps following CLEAN architecture principles?

I am building an Android app following the Clean Architecture Principles. Here is what I've got:

app module:

  • Contains all the Android dependencies.
  • Uses MVVM with ViewModel from the arch components.
  • ViewModels only communicate with UseCases, which are constructor injected.

usecase module:

  • Contains all of the use cases.
  • Use cases only communicate with Repositories, which are constructor injected.

repository module:

  • Contains all of the repositories.
  • Repositories communicate with web services or database etc.
  • I have a Retrofit interface defined in this layer, which the repository takes in it's constructor.

data module:

  • Contains all the data models

I am trying to use Hilt for dependency injection in the app. I don't want to expose Retrofit, OkHttp etc to the app module because I don't want developers to be able to put network code in the wrong module. Remember, the app module uses ViewModel which can ONLY talk to use cases.

How do I set this up? I tried putting dagger modules in each of these modules to define injection, then in the main app module I included the module from usecase:

@Module(includes = [UseCaseModule::class])
@InstallIn(ApplicationComponent::class)
object AppModule

but this does not work as it starts to complain about not being able to find transitive dependencies in the other modules that I want to keep hidden.

like image 666
Christopher Perry Avatar asked Nov 03 '20 20:11

Christopher Perry


People also ask

What is a multi module app?

A project with multiple Gradle modules is known as a multi-module project. In a multi-module project that ships as a single APK with no feature modules, it's common to have an app module that can depend on most modules of your project and a base or core module that the rest of the modules usually depend on.

What is dagger hilt?

Hilt provides a standard way to incorporate Dagger dependency injection into an Android application. The goals of Hilt are: To simplify Dagger-related infrastructure for Android apps. To create a standard set of components and scopes to ease setup, readability/understanding, and code sharing between apps.


Video Answer


2 Answers

TLDR: Use Hilt version 2.40+ which allows working with transitive dependencies


Since Hilt version 2.37 there's an gradle flag enableAggregatingTask which allows Hilt to collect transitive dependencies. The flag is enabled by default in version 2.40 and newer.

When you have your gradle modules as :app -> :usecase -> :repository -> :retrofit (external) and assuming you use implementation in your gradle scripts, your Retrofit instance can still be provided and injected in :repository module, but it won't leak into other modules.

So in case you want to encapsulate your Retrofit in your repository module, you may create

@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {

  @Provides
  fun provideRetrofit(retrofitBuilder: Retrofit.Builder): YourApi = retrofitBuilder.create<YourApi>()

}

You don't need to manually include your RepositoryModule in :app as this is done by Hilt and the @InstallIn annotation.

like image 182
mlykotom Avatar answered Oct 11 '22 09:10

mlykotom


Unfortunately, Hilt uses a monolithic approach currently. This means that your app module will have access to ALL your modules.

I don't want to expose Retrofit, OkHttp etc. to the app module because I don't want developers to be able to put network code in the wrong module.

No, you don't have to include your network-related classes in the app module. Rather, in the data module.

But bear in mind that the app module will still indirectly have access to Retrofit by implementing the data module for Hilt to work. You can check out this post.

like image 24
princessdharmy Avatar answered Oct 11 '22 10:10

princessdharmy