It is the small example how to build horizontal and vertical modules structure on the Android platform. Below represents modules dependencies. In general, they are split into three levels:
- Application level
- Features level
- Middleware level
Each level will be described below in more precise way.
Create stand-alone modules based on SOLID principles.
Decrease compilation time.
To create reusable components or templates for them.
To split responsibility between sub-teams.
To use different architectural patterns. Because sometimes can occur the situation when MVVM is more suitable than MVP and vice versa.
Support instant app.
App module has dependencies into all modules in the application. You can think about app module as the dirtiest of all the dirty components. The app module is the ultimate detail - the lowest-level policy. The main and single responsibility of this module to build dagger dependencies graph.
@Component(
modules = [
AndroidInjectionModule::class,
AndroidSupportInjectionModule::class,
AppConfigurationModule::class,
LoginFeatureModule::class,
DashboardFeatureModule::class,
IntentFactoryModule::class,
DatabaseModule::class
]
)
You should avoid keeping any resources files inside the module. In the case when a resource should be shared between features BaseFeature module will be the perfect place.
On this level we are going to declare your application features. The Example contains two feature:
- Login
- Dashboard
Besides that at this level also exists features' contracts module. Contract modules help to avoid cyclic dependencies between features when we need to navigation from feature A to B and then from B to A.
Usually, the contract contains the single interface with the method which returns intent to launch feature what implements this module. Also can be declared domain objects for communications.
Contract
interface LoginFlowIntentFactory {
fun createIntent(context: Context) : Intent
}
Feature
class LoginIntentFactory : LoginFlowIntentFactory {
override fun createIntent(context: Context): Intent {
return Intent(context, LoginActivity::class.java)
}
}
@Module
class LoginIntentFactoryModule {
@Provides
@Singleton
fun provideIntentFactory(): LoginFlowIntentFactory {
return LoginIntentFactory()
}
}
After that LoginIntentFactoryModule module should be declared in dagger component. When is needed to navigate to login feature, the contract will be declared into grade file.
implementation project(':feature:login_flow:login_contract')
As module knows about the intent factory and dagger module was put into the graph. The field can be injected.
At this level, the application contains modules which will be reusable across the application. For example, good candidates will be database module, analytics or even IOT module. Communication with Middleware modules follows The Dependency Inversion Principle.
With this approach, features can be tested independently. For this it is needed to add launch activity into feature manifest.