You are on page 1of 39

Independent Features

Powered by Dagger Extensions


@yahyabayramoglu
Live Demo
Why Independent Features?
Why Independent Features?
Technical Aspect

● Clear separation
● Put more thoughts in development
● Less entangled codebase

Practical Aspect

● Ownership
● Easy to reuse
● Isolated build
Feature Dependency Structure
Feature Dependency Structure
Requires
registration
Self
maintained

Application Feature

Input
Problem?
Problem?
@ApplicationScope
@Component(
modules = arrayOf(
NetworkModule::class,
FirebaseModule::class,
MapProviderModule::class,
OnlyFeature1RelatedModule::class,
ThenFeature2Module::class,
GlobalApiModule::class,
// ...
)
)
interface ApplicationComponent
Problem?
@ApplicationScope
@Component(
modules = arrayOf(
NetworkModule::class,
FirebaseModule::class,
MapProviderModule::class,
OnlyFeature1RelatedModule::class,
ThenFeature2Module::class,
GlobalApiModule::class,
// ...
)
)
interface ApplicationComponent
Solution?
@ApplicationScope
@Component(
modules = arrayOf(
NetworkModule::class,
FirebaseModule::class,
MapProviderModule::class,
),
dynamicModuleKey = arrayOf(
"Feature1",
"Feature2"
)
)
interface ApplicationComponent
Solution?
Dagger Extensions
Why Anvil and Hilt?
What do they bring to the table?

Inverse the relation between Component and Module


Anvil
Anvil
Create ApplicationComponent *
Application
Binding for components

Define feature scope & component


Feature1
Module to contribute ApplicationComponent

Define shared scopes


Base
Define shared abstractions
Anvil

@Scope data class Feature(


@MustBeDocumented val title: String,
@Retention val deeplinkHost: String
annotation class ApplicationScope )

interface FeatureDependencyProvider { interface ApplicationPlugin {


fun <T> dependencies(): T fun apply(application: Application)
} }

Base
Anvil
@ContributesTo(ApplicationScope::class)
@Module
class BaseModule {

@Provides
@ElementsIntoSet
fun provideFeatureSet(): Set<Feature> = emptySet()

@Provides
@ElementsIntoSet
fun provideApplicationPluginSet(): Set<ApplicationPlugin> = emptySet()
} Base
Anvil

@Scope
@MustBeDocumented
@Retention
annotation class Feature1Scope

@ContributesTo(ApplicationScope::class)
interface Feature1Dependencies {
fun sampleSharedLogger(): SampleSharedLogger
fun depsWithAppLifecycle(): DepsWithAppLifecycle
}

Feature 1
Anvil

@Feature1Scope
@MergeComponent(
scope = Feature1Scope::class,
dependencies = [Feature1Dependencies::class]
)
interface Feature1Component

Feature 1
Anvil
@ContributesTo(ApplicationScope::class)
@Module
class Feature1ModuleToApplication {

// ...

Feature 1
Anvil
@ContributesTo(ApplicationScope::class)
@Module
class Feature1ModuleToApplication {

@Provides
@IntoSet
fun provideFeature1(): Feature = Feature(
"Feature 1",
"feature1"
)
}
Feature 1
Anvil
@ContributesTo(ApplicationScope::class)
@Module
class Feature1ModuleToApplication {

@Provides
@IntoSet
fun provideFeature1ApplicationPlugin(): ApplicationPlugin =
Feature1ApplicationPlugin()

Feature 1
Anvil
@ContributesTo(ApplicationScope::class)
@Module
class Feature1ModuleToApplication {

@Provides
@ApplicationScope
fun provideDepsWithAppLifecycle(application: Application): DepsWithAppLifecycle =
DepsWithAppLifecycle(application)

Feature 1
Anvil
@ApplicationScope
@MergeComponent(scope = ApplicationScope::class)
interface ApplicationComponent

Application
Anvil your_module/build/tmp/kapt3/stubs/...

@Component(
modules = {
Feature1ModuleToApplication.class,
Feature2ModuleToApplication.class,
BaseModule.class
}, dependencies = {})
@ApplicationScope
public abstract interface ApplicationComponent
extends Feature1Dependencies, Feature2Dependencies {

Generated
Anvil
class SampleApplication : Application() {

// ...

Application
Anvil
class SampleApplication : Application() {

val applicationComponent = lazy {


DaggerApplicationComponent.builder()
.application(this)
.build()
}

Application
Anvil
class SampleApplication : Application() {

@Inject
lateinit var applicationPlugins: Set<@JvmSuppressWildcards ApplicationPlugin>

override fun onCreate() {


super.onCreate()

applicationComponent.value.inject(this)
applicationPlugins.forEach { it.apply(this) }
}
Application
}
Anvil
class SampleApplication : Application(), FeatureDependencyProvider {

@Suppress("UNCHECKED_CAST")
override fun <T> dependencies(): T {
return applicationComponent.value as? T
?: throw IllegalStateException(
"Feature does not provide"
+ " its dependencies to the ApplicationScope.")
}
}

Application
Hilt
Hilt

Application Add HiltAndroidApp annotation

Feature1
Module to contribute ApplicationComponent

Base
Define shared abstractions
Hilt

data class Feature(


val title: String,
val deeplinkHost: String
)

interface ApplicationPlugin {
fun apply(application: Application)
}

Base
Hilt
@InstallIn(ApplicationComponent::class)
@Module
class BaseModule {

@Provides
@ElementsIntoSet
fun provideFeatureSet(): Set<Feature> = emptySet()

@Provides
@ElementsIntoSet
fun provideApplicationPluginSet(): Set<ApplicationPlugin> = emptySet()
} Base
Hilt
@InstallIn(ApplicationComponent::class)
@Module
class Feature1ModuleToApplication {

@Provides
@IntoSet
fun provideFeature1(): Feature = Feature(
"Feature 1",
"feature1"
)
}
Feature 1
Hilt
@InstallIn(ApplicationComponent::class)
@Module
class Feature1ModuleToApplication {

@Provides
@IntoSet
fun provideFeature1ApplicationPlugin(): ApplicationPlugin =
Feature1ApplicationPlugin()

Feature 1
Hilt
@InstallIn(ApplicationComponent::class)
@Module
class Feature1ModuleToApplication {

@Provides
@Singleton
fun provideDepsWithAppLifecycle(application: Application): DepsWithAppLifecycle =
DepsWithAppLifecycle(application)

Feature 1
Hilt
@HiltAndroidApp
class SampleApplication : Application() {

@Inject
lateinit var applicationPlugins: Set<@JvmSuppressWildcards ApplicationPlugin>

override fun onCreate() {


super.onCreate()

applicationPlugins.forEach { it.apply(this) }
}
} Application
A new
perspective
for Dagger
by the
Extensions
Thank you!
● “Modularise in Structure” - 2019
https://bit.ly/3nBbslG
● “Building Features by Independent Dagger Components” - 2018
https://bit.ly/36TsXba
● About the ApplicationPlugin implementation
https://bit.ly/3129aCx
● Github repo for the sample application
https://github.com/yayaa/IndependentFeatures

@yahyabayramoglu

You might also like