SlideShare a Scribd company logo
Using Dagger in a
Clean Architecture project
Fabio Collini
@fabioCollini
linkedin.com/in/fabiocollini
github.com/fabioCollini
medium.com/@fabioCollini
Android programmazione avanzata
Android Developers Italia
Ego slide
entitiesentities
Layered

Architecture
Clean

Architecture
Dagger
Modularization
Modularization
https://www.youtube.com/watch?v=PZBg5DIzNww
https://jeroenmols.com/blog/2019/03/06/modularizationwhy/
1. Speeds up builds
2. Enable on demand delivery
3. Simplify development
4. Reuse modules across apps
5. Experiment with new technologies
6. Scale development teams
7. Enables refactoring
8. Simplifies test automation
Modularization - Why you should care
https://jeroenmols.com/blog/2019/03/06/modularizationwhy/
App
feature3feature1 feature2
dynamic
feature3
App
feature1 feature2
App
dynamic
feature3
feature1 feature2
Feature1App
Main app
BUILD SUCCESSFUL in 1m 12s

847 actionable tasks: 266 executed, 542 from cache, 39 up-to-date

Feature app
BUILD SUCCESSFUL in 43s

504 actionable tasks: 159 executed, 321 from cache, 24 up-to-date

Feature module
BUILD SUCCESSFUL in 30s

427 actionable tasks: 87 executed, 308 from cache, 32 up-to-date

Core module
BUILD SUCCESSFUL in 22s

308 actionable tasks: 63 executed, 220 from cache, 25 up-to-date

Utils
BUILD SUCCESSFUL in 6s

22 actionable tasks: 6 executed, 13 from cache, 3 up-to-date
Poorman'sbenchmark
Main app
BUILD SUCCESSFUL in 1m 12s

847 actionable tasks: 266 executed, 542 from cache, 39 up-to-date

Feature app
BUILD SUCCESSFUL in 43s

504 actionable tasks: 159 executed, 321 from cache, 24 up-to-date

Feature module
BUILD SUCCESSFUL in 30s

427 actionable tasks: 87 executed, 308 from cache, 32 up-to-date

Core module
BUILD SUCCESSFUL in 22s

308 actionable tasks: 63 executed, 220 from cache, 25 up-to-date

Utils
BUILD SUCCESSFUL in 6s

22 actionable tasks: 6 executed, 13 from cache, 3 up-to-date
Poorman'sbenchmark
Architectures
Activity
Repository
api1
UseCase
ViewModel
entitiesentitiesentitiesentities
domain
repository
UI
data source
presenter
Activity
Repository
api1
UseCase
ViewModel
entitiesentitiesentitiesentities
domain
repository
UI
data source
presenter
Activity
Repository
api1
UseCase
ViewModel
RepositoryImplRepositoryUseCase
domain repository
UseCase
RepositoryImpl
Repository
domain repository
Repository RepositoryImpl
UseCase
Inversion
Of Control
The “I” in S.O.L.I.D.
entitiesentitiesentitiesentities
domain
repository
UI
data source
presenter
Activity
Repository
api1
UseCase
ViewModel
TDD
Pros
Framework
independence
entitiesentitiesentitiesentities
domain
repository
UI
data source
presenter
https://www.youtube.com/watch?v=GlDsfq3xHvo&t=
Dagger
App
dynamic
feature3
feature1 feature2
Feature1App
App
dynamic
feature3
feature1 feature2
Feature1App
Dagger Dagger
Dagger
Dagger
Dagger
Dagger
Dagger
Dagger
Dagger
Dagger
Dagger
Dagger
Component dependencies
Subcomponents
Dagger Android
Daggerpatterns
Component dependencies
https://medium.com/androiddevelopers/dependency-injection-in-a-multi-module-project-1a09511c14b7
https://proandroiddev.com/using-dagger-in-a-multi-module-project-1e6af8f06ffc
DaggerTip#1
@Component
interface Component1
@Component(dependencies = [Component1::class])
interface Component2
fun main() {
DaggerComponent2.builder()
.build()
}1
DaggerTip#1
Exception in thread "main" java.lang.IllegalStateException: Component1 must be set
at dagger.internal.Preconditions.checkBuilderRequirement(Preconditions.java:95)
at DaggerComponent2$Builder.build(DaggerComponent2.java:35)
@Component
interface Component1
@Component(dependencies = [Component1::class])
interface Component2
fun main() {
DaggerComponent2.builder()
.build()
}1
DaggerTip#1
Exception in thread "main" java.lang.IllegalStateException: Component1 must be set
at dagger.internal.Preconditions.checkBuilderRequirement(Preconditions.java:95)
at DaggerComponent2$Builder.build(DaggerComponent2.java:35)
@Component
interface Component1
@Component(dependencies = [Component1::class])
interface Component2
fun main() {
DaggerComponent2.builder()
.component1(DaggerComponent1.create())
.build()
}1
DaggerTip#1
@Component
interface Component1
@Component(dependencies = [Component1::class])
interface Component2 {
@Component.Factory
interface Factory {
fun create(component1: Component1): Component2
}2
}3
fun main() {
DaggerComponent2.factory()
.create(DaggerComponent1.create())
}1
DaggerTip#2
@Module
class Module1 {
@Provides
fun pattern() = "#,###.00"
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}1
Module1
pattern
format
DaggerTip#2
@Module
class Module1 {
@Provides
fun pattern() = "#,###.00"
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}1
@Component(modules = [Module1::class])
interface Component1 {
val format: DecimalFormat
}2
Module1
Component1
pattern
format
format
DaggerTip#2 @Module
class Module1 {
@Provides
fun pattern() = "#,###.00"
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}1
@Component(modules = [Module1::class])
interface Component1 {
val format: DecimalFormat
}2
@Module
class Module2 {
}3
@Component(
modules = [Module2::class],
dependencies = [Component1::class]
)5
interface Component2 {
}4
Module2
Component2
Module1
Component1
pattern
format
format
DaggerTip#2 @Module
class Module1 {
@Provides
fun pattern() = "#,###.00"
}1
@Component(modules = [Module1::class])
interface Component1 {
}2
@Module
class Module2 {
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}3
@Component(
modules = [Module2::class],
dependencies = [Component1::class]
)5
interface Component2 {
val format: DecimalFormat
}4
Module2
Component2
format
Module1
Component1
pattern
format
error: [Dagger/MissingBinding] String cannot be provided without an @Inject constructor or
an @Provides-annotated method.
public abstract interface Component2 {
^
String is injected at
Module2.format(pattern)
DecimalFormat is provided at
Component2.getFormat()
DaggerTip#2 @Module
class Module1 {
@Provides
fun pattern() = "#,###.00"
}1
@Component(modules = [Module1::class])
interface Component1 {
val pattern: String
}2
@Module
class Module2 {
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}3
@Component(
modules = [Module2::class],
dependencies = [Component1::class]
)5
interface Component2 {
val format: DecimalFormat
}4
Module2
Component2
format
Module1
Component1
patternpattern
format
DaggerTip#2 @Module
class Module1 {
@Provides
fun pattern() = "#,###.00"
}1
@Component(modules = [Module1::class])
interface Component1 {
val pattern: String
}2
@Module
class Module2 {
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}3
@Component(
modules = [Module2::class],
dependencies = [Component1::class]
)5
interface Component2 {
val format: DecimalFormat
}4
Module2
Component2
format
Module1
Component1
patternpattern
format
DaggerTip#3
interface Component1 {
val pattern: String
}2
@Module
class Module2 {
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}3
@Component(
modules = [Module2::class],
dependencies = [Component1::class]
)5
interface Component2 {
val format: DecimalFormat
}4
DaggerTip#3 interface Component1 {
val pattern: String
}2
@Module
class Module2 {
@Provides
fun format(pattern: String) =
DecimalFormat(pattern)
}3
@Component(
modules = [Module2::class],
dependencies = [Component1::class]
)5
interface Component2 {
val format: DecimalFormat
}4
fun main() {
val component1 = object : Component1 {
override val pattern ="#,###.0000"
}3
val component2 = DaggerComponent2.factory().create(component1)
}4
@Module
class LibModule {
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
Lib
@Module
class LibModule {
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
Lib
feature2
Lib
feature1
feature2
Lib
feature1
@Component(dependencies = [LibComponent::class])
interface Feature1Component
@Component(dependencies = [LibComponent::class])
interface Feature2Component
@Module
class LibModule {
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
feat1feat2Lib
feature2
Lib
feature1
@Singleton
@Component(dependencies = [LibComponent::class])
interface Feature1Component
@Singleton
@Component(dependencies = [LibComponent::class])
interface Feature2Component
@Module
class LibModule {
@Singleton
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Singleton
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
Libfeat1feat2
feature2
Lib
feature1
@Scope annotation class Feature1SingletonScope
@Feature1SingletonScope
@Component(dependencies = [LibComponent::class])
interface Feature1Component
@Scope annotation class Feature2SingletonScope
@Feature2SingletonScope
@Component(dependencies = [LibComponent::class])
interface Feature2Component
@Scope annotation class LibSingletonScope
@Module
class LibModule {
@LibSingletonScope
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@LibSingletonScope
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
Libfeat1feat2
DaggerTip#4
@Module
class LibModule {
@Singleton
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Singleton
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
DaggerTip#4
@Module
class LibModule {
@Singleton
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Singleton
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
fun main() {
val obj1 = DaggerLibComponent.create().myLibObject
val obj2 = DaggerLibComponent.create().myLibObject
println(obj1 === obj2)
//false :(
}2
DaggerTip#4
@Module
class LibModule {
@Singleton
@Provides
fun provideMyLibObject() = MyLibObject()
}3
@Singleton
@Component(modules = [LibModule::class])
interface LibComponent {
val myLibObject: MyLibObject
}1
fun main() {
val component = DaggerLibComponent.create()
val obj1 = component.myLibObject
val obj2 = component.myLibObject
println(obj1 === obj2)
//true :)
}2
interface LibComponentProvider {
val libComponent: LibComponent
}8
Lib
interface Feature1Provider {
val feature1Component: Feature1Component
}7
interface LibComponentProvider {
val libComponent: LibComponent
}8
feature1Lib
class MyApp : Application(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}6
interface Feature1Provider {
val feature1Component: Feature1Component
}7
interface LibComponentProvider {
val libComponent: LibComponent
}8
feature1LibApp
class MyApp : Application(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}6
interface Feature1Provider {
val feature1Component: Feature1Component
}7
fun Application.feature1Component() =
(this as Feature1Provider).feature1Component
interface LibComponentProvider {
val libComponent: LibComponent
}8
fun Application.libComponent() =
(this as LibComponentProvider).libComponent
Libfeature1App
class MyApp : Application(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}6
class MyFeature1App : Application(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}3
interface Feature1Provider {
val feature1Component: Feature1Component
}7
fun Application.feature1Component() =
(this as Feature1Provider).feature1Component
interface LibComponentProvider {
val libComponent: LibComponent
}8
fun Application.libComponent() =
(this as LibComponentProvider).libComponent
Libfeature1Feature1AppApp
interface ComponentHolder {
val map: MutableMap<KClass<*>, Any>
}1
open class ComponentHolderApp : Application(), ComponentHolder {
override val map = mutableMapOf<KClass<*>, Any>()
}2
inline fun <reified C : Any> ComponentHolder.getOrCreate(factory: () -> C): C =
map.getOrPut(C::class, factory) as C
A “real” implementation is available here:
https://github.com/fabioCollini/CleanWeather/blob/dagger/kotlinUtils/
src/main/java/it/codingjam/cleanweather/kotlinutils/ComponentHolder.kt
class MyApp : Application(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}6
class MyFeature1App : Application(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}3
interface Feature1Provider {
val feature1Component: Feature1Component
}7
fun Application.feature1Component() =
(this as Feature1Provider).feature1Component
interface LibComponentProvider {
val libComponent: LibComponent
}8
fun Application.libComponent() =
(this as LibComponentProvider).libComponent
Libfeature1Feature1AppApp
class MyApp : ComponentHolderApp(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}6
class MyFeature1App : ComponentHolderApp(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}3
interface Feature1Provider {
val feature1Component: Feature1Component
}7
fun ComponentHolderApp.feature1Component() =
(this as Feature1Provider).feature1Component
interface LibComponentProvider {
val libComponent: LibComponent
}8
fun ComponentHolderApp.libComponent() =
(this as LibComponentProvider).libComponent
Libfeature1Feature1AppApp
class MyApp : ComponentHolderApp(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}6
class MyFeature1App : ComponentHolderApp(), LibComponentProvider, Feature1Provider {
override val libComponent =
DaggerLibComponent.create()
override val feature1Component =
DaggerFeature1Component.factory().create(libComponent)
}3
interface Feature1Provider {
val feature1Component: Feature1Component
}7
fun ComponentHolderApp.feature1Component() = getOrCreate {
}1
interface LibComponentProvider {
val libComponent: LibComponent
}8
fun ComponentHolderApp.libComponent() = getOrCreate {
}2
Libfeature1Feature1AppApp
class MyApp : ComponentHolderApp(), LibComponentProvider, Feature1Provider
class MyFeature1App : ComponentHolderApp(), LibComponentProvider, Feature1Provider
interface Feature1Provider {
val feature1Component: Feature1Component
}7
fun ComponentHolderApp.feature1Component() = getOrCreate {
DaggerFeature1Component.factory()
.create(libComponent())
}1
interface LibComponentProvider {
val libComponent: LibComponent
}8
fun ComponentHolderApp.libComponent() = getOrCreate {
DaggerLibComponent.create()
}2
Feature1App
App Libfeature1
class MyApp : ComponentHolderApp()
class MyFeature1App : ComponentHolderApp()
fun ComponentHolderApp.feature1Component() = getOrCreate {
DaggerFeature1Component.factory()
.create(libComponent())
}1
fun ComponentHolderApp.libComponent() = getOrCreate {
DaggerLibComponent.create()
}2
Libfeature1
Feature1App
App
@Feature1SingletonScope
@Component(dependencies = [Lib1Component::class, Lib2Component::class])
interface Feature1Component {
//...
}
DaggerTip#5
error: @Feature1SingletonScope Feature1Component depends on more than one scoped component:
@dagger.Component(dependencies = {Lib1Component.class, Lib2Component.class})
^
@Lib1SingletonScope Lib1Component
@Lib2SingletonScope Lib2Component
@Lib1SingletonScope
@Component(modules = [Lib1Module::class])
interface Lib1Component {
val myLibObject: MyLibObject
}2
DaggerTip#5
interface Lib1Component {
val myLibObject: MyLibObject
}2
@Lib1SingletonScope
@Component(modules = [Lib1Module::class])
interface Lib1ComponentImpl : Lib1Component
DaggerTip#5
@Scope
annotation class Lib1SingletonScope
@Module
class Lib1Module {
@Lib1SingletonScope
@Provides
fun provideMyLibObject() = MyLibObject()
}1
interface Lib1Component {
val myLibObject: MyLibObject
}2
@Lib1SingletonScope
@Component(modules = [Lib1Module::class])
interface Lib1ComponentImpl : Lib1Component
fun ComponentHolderApp.lib1Component(): Lib1Component =
getOrCreate { DaggerLib1ComponentImpl.create() }
DaggerTip#5
@Scope
internal annotation class Lib1SingletonScope
@Module
internal class Lib1Module {
@Lib1SingletonScope
@Provides
fun provideMyLibObject() = MyLibObject()
}1
interface Lib1Component {
val myLibObject: MyLibObject
}2
@Lib1SingletonScope
@Component(modules = [Lib1Module::class])
internal interface Lib1ComponentImpl : Lib1Component
fun ComponentHolderApp.lib1Component(): Lib1Component =
getOrCreate { DaggerLib1ComponentImpl.create() }
DaggerTip#5
DaggerTip#5
interface Lib1Component {
val myLibObject: MyLibObject
}2
fun ComponentHolderApp.lib1Component(): Lib1Component =
getOrCreate { DaggerLib1ComponentImpl.create() }
NODaggerTip
interface Lib1Component {
val myLibObject: MyLibObject
}2
fun ComponentHolderApp.lib1Component(): Lib1Component =
getOrCreate { DaggerLib1ComponentImpl.create() }
NODaggerTip
interface Lib1Component {
val myLibObject: MyLibObject
}2
private class Lib1ComponentImpl : Lib1Component {
override val myLibObject by lazy {
MyLibObject()
}3
}4
fun ComponentHolderApp.lib1Component(): Lib1Component =
getOrCreate { Lib1ComponentImpl() }
Clean
Architecture
domain repository
Repository RepositoryImpl
UseCase
class UseCase @Inject constructor(
val repository: Repository) {
//...
}9
domain
domain repository
Repository RepositoryImpl
UseCase
class UseCase @Inject constructor(
val repository: Repository) {
//...
}9
@Component
interface DomainComponent {
val useCase: UseCase
}2
domain
domain repository
Repository RepositoryImpl
UseCase
Domain
Component
class UseCase @Inject constructor(
val repository: Repository) {
//...
}9
@Module
class DomainModule {
@Provides
fun provideRepository(): Repository =
RepositoryImpl()
}1
@Component(modules = [DomainModule::class])
interface DomainComponent {
val useCase: UseCase
}2
domain
domain repository
Repository RepositoryImpl
UseCase
Domain
Component
@Component
interface DomainComponent {
val useCase: UseCase
}2
domain
domain repository
Repository RepositoryImpl
UseCase
Domain
Component
interface DomainDependencies {
val repository: Repository
}3
@Component(
dependencies = [DomainDependencies::class]
)9
interface DomainComponent {
val useCase: UseCase
}2
domain
domain repository
Repository RepositoryImpl
UseCase
Domain
Dependencies
Domain
Component
interface DomainDependencies {
val repository: Repository
}3
@Component(
dependencies = [DomainDependencies::class]
)9
interface DomainComponent {
val useCase: UseCase
}2
@Component
interface RepositoryComponent : DomainDependencies
domainrepo
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
???
)3
}4
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
domain
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
map[DomainDependencies::class]
as DomainDependencies
)3
}4
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
domain
class MyApp : ComponentHolderApp() {
override fun onCreate() {
map[DomainDependencies::class] =
DaggerRepositoryComponent.create()
}1
}21
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
map[DomainDependencies::class]
as DomainDependencies
)3
}4
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
domainApp
interface DomainDependencies {
val repository: Repository
}2
@Component(dependencies = [DomainDependencies::class])
interface DomainComponent {
val useCase: UseCase
}3
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
???
)4
}5
@Component
interface RepositoryComponent : DomainDependencies
domainrepo
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
interface DomainDependencies {
val repository: Repository
}2
@get:InversionDef
val ComponentHolderApp.domainDependencies
by Inversion.of(DomainDependencies::class)
@Component(dependencies = [DomainDependencies::class])
interface DomainComponent {
val useCase: UseCase
}3
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
???
)4
}5
@Component
interface RepositoryComponent : DomainDependencies
domainrepo
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
interface DomainDependencies {
val repository: Repository
}2
@get:InversionDef
val ComponentHolderApp.domainDependencies
by Inversion.of(DomainDependencies::class)
@Component(dependencies = [DomainDependencies::class])
interface DomainComponent {
val useCase: UseCase
}3
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
domainDependencies()
)4
}5
@Component
interface RepositoryComponent : DomainDependencies
domainrepo
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
interface DomainDependencies {
val repository: Repository
}2
@get:InversionDef
val ComponentHolderApp.domainDependencies
by Inversion.of(DomainDependencies::class)
@Component(dependencies = [DomainDependencies::class])
interface DomainComponent {
val useCase: UseCase
}3
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
domainDependencies()
)4
}5
@Component
interface RepositoryComponent : DomainDependencies
@InversionProvider
fun ComponentHolderApp.provideDomainDependencies():
DomainDependencies = getOrCreate {
DaggerRepositoryComponent.create()
}6
domainrepo
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
interface DomainDependencies {
val repository: Repository
}2
@get:InversionDef
val ComponentHolderApp.domainDependencies
by Inversion.of(DomainDependencies::class)
@Component(dependencies = [DomainDependencies::class])
interface DomainComponent {
val useCase: UseCase
}3
fun ComponentHolderApp.domainComponent() = getOrCreate {
DaggerDomainComponent.factory().create(
domainDependencies()
)4
}5
@Component
interface RepositoryComponent : DomainDependencies
@InversionProvider
fun ComponentHolderApp.provideDomainDependencies():
DomainDependencies = getOrCreate {
DaggerRepositoryComponent.create()
}6
domainrepo
domain repository
Repository RepositoryImpl
UseCase
Repository
Component
Domain
Dependencies
Domain
Component
Testing
class MainActivity : AppCompatActivity() {
@Inject
lateinit var useCase: UseCase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val activityComponent = DaggerMainActivityComponent.factory().create(
(application as ComponentHolderApp).domainComponent(),
this
)1
activityComponent.inject(this)
setContentView(R.layout.activity_main)
findViewById<TextView>(R.id.text).text = useCase.retrieve()
}2
}3
class MainActivity : AppCompatActivity() {
@Inject
lateinit var useCase: UseCase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val activityComponent = DaggerMainActivityComponent.factory().create(
(application as ComponentHolderApp).domainComponent(),
this
)1
activityComponent.inject(this)
setContentView(R.layout.activity_main)
findViewById<TextView>(R.id.text).text = useCase.retrieve()
}2
}3
class MainActivity : AppCompatActivity() {
@Inject
lateinit var useCase: UseCase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val activityComponent = DaggerMainActivityComponent.factory().create(
(application as ComponentHolderApp).domainComponent(),
this
)1
activityComponent.inject(this)
setContentView(R.layout.activity_main)
findViewById<TextView>(R.id.text).text = useCase.retrieve()
}2
}3
class MainActivityTest {
@get:Rule
val rule = ActivityTestRule(MainActivity::class.java, false, false)
private val useCaseMock = mock<UseCase>()
}4
class MainActivityTest {
@get:Rule
val rule = ActivityTestRule(MainActivity::class.java, false, false)
private val useCaseMock = mock<UseCase>()
@Before
fun setUp() {
}2
}4
class MainActivityTest {
@get:Rule
val rule = ActivityTestRule(MainActivity::class.java, false, false)
private val useCaseMock = mock<UseCase>()
@Before
fun setUp() {
val app = ApplicationProvider.getApplicationContext<ComponentHolderApp>()
app.map.clear()
app.map[DomainComponent::class] = object : DomainComponent {
override val useCase = useCaseMock
}1
}2
}4
class MainActivityTest {
@get:Rule
val rule = ActivityTestRule(MainActivity::class.java, false, false)
private val useCaseMock = mock<UseCase>()
@Before
fun setUp() {
val app = ApplicationProvider.getApplicationContext<ComponentHolderApp>()
app.map.clear()
app.map[DomainComponent::class] = object : DomainComponent {
override val useCase = useCaseMock
}1
}2
@Test
fun launchActivity() {
whenever(useCaseMock.retrieve()) doReturn "ABCDEF"
rule.launchActivity(null)
onView(withId(R.id.text))
.check(matches(withText("ABCDEF")))
}3
}4
class MainActivityTest {
@get:Rule
val rule = ActivityTestRule(MainActivity::class.java, false, false)
private val useCaseMock = mock<UseCase>()
@Before
fun setUp() {
val app = ApplicationProvider.getApplicationContext<ComponentHolderApp>()
app.map.clear()
app.map[DomainComponent::class] = object : DomainComponent {
override val useCase = useCaseMock
}1
}2
@Test
fun launchActivity() {
whenever(useCaseMock.retrieve()) doReturn "ABCDEF"
rule.launchActivity(null)
onView(withId(R.id.text))
.check(matches(withText("ABCDEF")))
}3
}4
Wrappingup
Component dependencies
Each module exposes:
a Component interface
a method to create the Component
A map in the App can be useful to:
manage singletons
replace real objects with fakes/mocks
Links
Demo Project
github.com/fabioCollini/CleanWeather/tree/dagger
Droidcon Italy talk - SOLID principles in practice: the Clean Architecture
youtube.com/watch?v=GlDsfq3xHvo&t=
Implementing Dependency Inversion using Dagger components
medium.com/google-developer-experts/implementing-dependency-inversion-using-dagger-components-d6b0fb3b6b5e
Inversion library
github.com/fabioCollini/Inversion
Yigit Boyar, Florina Muntenescu - Build a Modular Android App Architecture (Google I/O'19)
youtube.com/watch?v=PZBg5DIzNww
Jeroen Mols - Modularization - Why you should care
jeroenmols.com/blog/2019/03/06/modularizationwhy/
Ben Weiss - Dependency injection in a multi module project
medium.com/androiddevelopers/dependency-injection-in-a-multi-module-project-1a09511c14b7
Marcos Holgado - Using Dagger in a multi-module project
proandroiddev.com/using-dagger-in-a-multi-module-project-1e6af8f06ffc
THANKS
FOR YOUR
ATTENTION
QUESTIONS?
@fabioCollini

More Related Content

What's hot

How to Split Your System into Microservices
How to Split Your System into MicroservicesHow to Split Your System into Microservices
How to Split Your System into Microservices
Eberhard Wolff
 
Critical system
Critical systemCritical system
Critical system
hamzaeng
 
What is component testing | David Tzemach
What is component testing | David TzemachWhat is component testing | David Tzemach
What is component testing | David Tzemach
David Tzemach
 
Reactive state management with Jetpack Components
Reactive state management with Jetpack ComponentsReactive state management with Jetpack Components
Reactive state management with Jetpack Components
Gabor Varadi
 
Resume_QA Analyst_Projects__VurukutiNarasingaRao
Resume_QA Analyst_Projects__VurukutiNarasingaRaoResume_QA Analyst_Projects__VurukutiNarasingaRao
Resume_QA Analyst_Projects__VurukutiNarasingaRaoNARASINGA RAO VURUKUTI
 
Concurrency in Swift
Concurrency in SwiftConcurrency in Swift
Concurrency in Swift
Seven Peaks Speaks
 
8 - Architetture Software - Architecture centric processes
8 - Architetture Software - Architecture centric processes8 - Architetture Software - Architecture centric processes
8 - Architetture Software - Architecture centric processesMajong DevJfu
 
Planejamento de Testes
Planejamento de TestesPlanejamento de Testes
Planejamento de Testeselliando dias
 
Selenium - WebDriver
Selenium - WebDriverSelenium - WebDriver
Selenium - WebDriver
Rodrigo Branas
 
Mortal Sins and Guilty Pleasures of Automation Engineers
Mortal Sins and Guilty Pleasures of Automation EngineersMortal Sins and Guilty Pleasures of Automation Engineers
Mortal Sins and Guilty Pleasures of Automation EngineersÞorgeir Ingvarsson
 
JIRA Zephyr - Test Management
JIRA Zephyr - Test ManagementJIRA Zephyr - Test Management
JIRA Zephyr - Test Management
Onlio
 
Lezione 11: Accesso ai RESTful Web Services in Java
Lezione 11: Accesso ai RESTful Web Services in JavaLezione 11: Accesso ai RESTful Web Services in Java
Lezione 11: Accesso ai RESTful Web Services in Java
Andrea Della Corte
 
Javascript best practices
Javascript best practicesJavascript best practices
Javascript best practices
Jayanga V. Liyanage
 
Guide to Destroying Codebases The Demise of Clever Code
Guide to Destroying Codebases   The Demise of Clever CodeGuide to Destroying Codebases   The Demise of Clever Code
Guide to Destroying Codebases The Demise of Clever Code
Gabor Varadi
 
8._MANAJEMEN_RISIKO.ppt
8._MANAJEMEN_RISIKO.ppt8._MANAJEMEN_RISIKO.ppt
8._MANAJEMEN_RISIKO.ppt
IanMardiansyah2
 
Application Lifecycle Management
Application Lifecycle ManagementApplication Lifecycle Management
Application Lifecycle Management
Amazon Web Services
 
Prezentacja - Zawód programista - obalamy mity!
Prezentacja - Zawód programista - obalamy mity!Prezentacja - Zawód programista - obalamy mity!
Prezentacja - Zawód programista - obalamy mity!
mamopracuj
 
Lo stato dell' arte sulla documentazione dei progetti ICT
Lo stato dell' arte sulla documentazione dei progetti ICTLo stato dell' arte sulla documentazione dei progetti ICT
Lo stato dell' arte sulla documentazione dei progetti ICT
Matteo Gentile
 

What's hot (20)

How to Split Your System into Microservices
How to Split Your System into MicroservicesHow to Split Your System into Microservices
How to Split Your System into Microservices
 
Critical system
Critical systemCritical system
Critical system
 
What is component testing | David Tzemach
What is component testing | David TzemachWhat is component testing | David Tzemach
What is component testing | David Tzemach
 
Reactive state management with Jetpack Components
Reactive state management with Jetpack ComponentsReactive state management with Jetpack Components
Reactive state management with Jetpack Components
 
Resume_QA Analyst_Projects__VurukutiNarasingaRao
Resume_QA Analyst_Projects__VurukutiNarasingaRaoResume_QA Analyst_Projects__VurukutiNarasingaRao
Resume_QA Analyst_Projects__VurukutiNarasingaRao
 
Concurrency in Swift
Concurrency in SwiftConcurrency in Swift
Concurrency in Swift
 
8 - Architetture Software - Architecture centric processes
8 - Architetture Software - Architecture centric processes8 - Architetture Software - Architecture centric processes
8 - Architetture Software - Architecture centric processes
 
Plano de teste
Plano de testePlano de teste
Plano de teste
 
Planejamento de Testes
Planejamento de TestesPlanejamento de Testes
Planejamento de Testes
 
Selenium - WebDriver
Selenium - WebDriverSelenium - WebDriver
Selenium - WebDriver
 
Mortal Sins and Guilty Pleasures of Automation Engineers
Mortal Sins and Guilty Pleasures of Automation EngineersMortal Sins and Guilty Pleasures of Automation Engineers
Mortal Sins and Guilty Pleasures of Automation Engineers
 
JIRA Zephyr - Test Management
JIRA Zephyr - Test ManagementJIRA Zephyr - Test Management
JIRA Zephyr - Test Management
 
Lezione 11: Accesso ai RESTful Web Services in Java
Lezione 11: Accesso ai RESTful Web Services in JavaLezione 11: Accesso ai RESTful Web Services in Java
Lezione 11: Accesso ai RESTful Web Services in Java
 
Javascript best practices
Javascript best practicesJavascript best practices
Javascript best practices
 
Guide to Destroying Codebases The Demise of Clever Code
Guide to Destroying Codebases   The Demise of Clever CodeGuide to Destroying Codebases   The Demise of Clever Code
Guide to Destroying Codebases The Demise of Clever Code
 
8._MANAJEMEN_RISIKO.ppt
8._MANAJEMEN_RISIKO.ppt8._MANAJEMEN_RISIKO.ppt
8._MANAJEMEN_RISIKO.ppt
 
Application Lifecycle Management
Application Lifecycle ManagementApplication Lifecycle Management
Application Lifecycle Management
 
Prezentacja - Zawód programista - obalamy mity!
Prezentacja - Zawód programista - obalamy mity!Prezentacja - Zawód programista - obalamy mity!
Prezentacja - Zawód programista - obalamy mity!
 
Shylaja_Sr QA 6 years
Shylaja_Sr QA 6 yearsShylaja_Sr QA 6 years
Shylaja_Sr QA 6 years
 
Lo stato dell' arte sulla documentazione dei progetti ICT
Lo stato dell' arte sulla documentazione dei progetti ICTLo stato dell' arte sulla documentazione dei progetti ICT
Lo stato dell' arte sulla documentazione dei progetti ICT
 

Similar to Using Dagger in a Clean Architecture project

Android App Architecture with modern libs in practice. Our way in R.I.D., Ser...
Android App Architecture with modern libs in practice. Our way in R.I.D., Ser...Android App Architecture with modern libs in practice. Our way in R.I.D., Ser...
Android App Architecture with modern libs in practice. Our way in R.I.D., Ser...
Sigma Software
 
Android MVVM architecture using Kotlin, Dagger2, LiveData, MediatorLiveData
Android MVVM architecture using Kotlin, Dagger2, LiveData, MediatorLiveDataAndroid MVVM architecture using Kotlin, Dagger2, LiveData, MediatorLiveData
Android MVVM architecture using Kotlin, Dagger2, LiveData, MediatorLiveData
Waheed Nazir
 
Dark side of Android apps modularization
Dark side of Android apps modularizationDark side of Android apps modularization
Dark side of Android apps modularization
David Bilík
 
Spring boot
Spring bootSpring boot
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Nicolas HAAN
 
Angular2 + rxjs
Angular2 + rxjsAngular2 + rxjs
Angular2 + rxjs
Christoffer Noring
 
Flutter technology Based on Web Development
Flutter technology Based on Web Development Flutter technology Based on Web Development
Flutter technology Based on Web Development
divyawani2
 
Extending Appcelerator Titanium Mobile through Native Modules
Extending Appcelerator Titanium Mobile through Native ModulesExtending Appcelerator Titanium Mobile through Native Modules
Extending Appcelerator Titanium Mobile through Native Modules
omorandi
 
OpenDaylight Developer Experience 2.0
 OpenDaylight Developer Experience 2.0 OpenDaylight Developer Experience 2.0
OpenDaylight Developer Experience 2.0
Michael Vorburger
 
How to code to code less
How to code to code lessHow to code to code less
How to code to code less
Anton Novikau
 
IRJET- Polymer Javascript
IRJET- Polymer JavascriptIRJET- Polymer Javascript
IRJET- Polymer Javascript
IRJET Journal
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)
Jose Manuel Pereira Garcia
 
Building scalable applications with angular js
Building scalable applications with angular jsBuilding scalable applications with angular js
Building scalable applications with angular js
Andrew Alpert
 
ScalaUA - distage: Staged Dependency Injection
ScalaUA - distage: Staged Dependency InjectionScalaUA - distage: Staged Dependency Injection
ScalaUA - distage: Staged Dependency Injection
7mind
 
Angular2 with type script
Angular2 with type scriptAngular2 with type script
Angular2 with type script
Ravi Mone
 
Rapid Prototyping with TurboGears2
Rapid Prototyping with TurboGears2Rapid Prototyping with TurboGears2
Rapid Prototyping with TurboGears2Alessandro Molina
 
Modern JavaScript Applications: Design Patterns
Modern JavaScript Applications: Design PatternsModern JavaScript Applications: Design Patterns
Modern JavaScript Applications: Design Patterns
Volodymyr Voytyshyn
 
Evolution of Patterns
Evolution of PatternsEvolution of Patterns
Evolution of Patterns
Chris Eargle
 
Use Eclipse technologies to build a modern embedded IDE
Use Eclipse technologies to build a modern embedded IDEUse Eclipse technologies to build a modern embedded IDE
Use Eclipse technologies to build a modern embedded IDEBenjamin Cabé
 
Lunch and learn as3_frameworks
Lunch and learn as3_frameworksLunch and learn as3_frameworks
Lunch and learn as3_frameworks
Yuri Visser
 

Similar to Using Dagger in a Clean Architecture project (20)

Android App Architecture with modern libs in practice. Our way in R.I.D., Ser...
Android App Architecture with modern libs in practice. Our way in R.I.D., Ser...Android App Architecture with modern libs in practice. Our way in R.I.D., Ser...
Android App Architecture with modern libs in practice. Our way in R.I.D., Ser...
 
Android MVVM architecture using Kotlin, Dagger2, LiveData, MediatorLiveData
Android MVVM architecture using Kotlin, Dagger2, LiveData, MediatorLiveDataAndroid MVVM architecture using Kotlin, Dagger2, LiveData, MediatorLiveData
Android MVVM architecture using Kotlin, Dagger2, LiveData, MediatorLiveData
 
Dark side of Android apps modularization
Dark side of Android apps modularizationDark side of Android apps modularization
Dark side of Android apps modularization
 
Spring boot
Spring bootSpring boot
Spring boot
 
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
 
Angular2 + rxjs
Angular2 + rxjsAngular2 + rxjs
Angular2 + rxjs
 
Flutter technology Based on Web Development
Flutter technology Based on Web Development Flutter technology Based on Web Development
Flutter technology Based on Web Development
 
Extending Appcelerator Titanium Mobile through Native Modules
Extending Appcelerator Titanium Mobile through Native ModulesExtending Appcelerator Titanium Mobile through Native Modules
Extending Appcelerator Titanium Mobile through Native Modules
 
OpenDaylight Developer Experience 2.0
 OpenDaylight Developer Experience 2.0 OpenDaylight Developer Experience 2.0
OpenDaylight Developer Experience 2.0
 
How to code to code less
How to code to code lessHow to code to code less
How to code to code less
 
IRJET- Polymer Javascript
IRJET- Polymer JavascriptIRJET- Polymer Javascript
IRJET- Polymer Javascript
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)
 
Building scalable applications with angular js
Building scalable applications with angular jsBuilding scalable applications with angular js
Building scalable applications with angular js
 
ScalaUA - distage: Staged Dependency Injection
ScalaUA - distage: Staged Dependency InjectionScalaUA - distage: Staged Dependency Injection
ScalaUA - distage: Staged Dependency Injection
 
Angular2 with type script
Angular2 with type scriptAngular2 with type script
Angular2 with type script
 
Rapid Prototyping with TurboGears2
Rapid Prototyping with TurboGears2Rapid Prototyping with TurboGears2
Rapid Prototyping with TurboGears2
 
Modern JavaScript Applications: Design Patterns
Modern JavaScript Applications: Design PatternsModern JavaScript Applications: Design Patterns
Modern JavaScript Applications: Design Patterns
 
Evolution of Patterns
Evolution of PatternsEvolution of Patterns
Evolution of Patterns
 
Use Eclipse technologies to build a modern embedded IDE
Use Eclipse technologies to build a modern embedded IDEUse Eclipse technologies to build a modern embedded IDE
Use Eclipse technologies to build a modern embedded IDE
 
Lunch and learn as3_frameworks
Lunch and learn as3_frameworksLunch and learn as3_frameworks
Lunch and learn as3_frameworks
 

More from Fabio Collini

Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
Fabio Collini
 
Managing parallelism using coroutines
Managing parallelism using coroutinesManaging parallelism using coroutines
Managing parallelism using coroutines
Fabio Collini
 
Kotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community confKotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community conf
Fabio Collini
 
Kotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere StockholmKotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere Stockholm
Fabio Collini
 
Solid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon ItalySolid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon Italy
Fabio Collini
 
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila RomagnaSOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
Fabio Collini
 
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean ArchitectureSOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean Architecture
Fabio Collini
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFrom Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
Fabio Collini
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Fabio Collini
 
Recap Google I/O 2018
Recap Google I/O 2018Recap Google I/O 2018
Recap Google I/O 2018
Fabio Collini
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
Fabio Collini
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+k
Fabio Collini
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UK
Fabio Collini
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2
Fabio Collini
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJava
Fabio Collini
 
Android Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUKAndroid Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUK
Fabio Collini
 
Data Binding in Action using MVVM pattern
Data Binding in Action using MVVM patternData Binding in Action using MVVM pattern
Data Binding in Action using MVVM pattern
Fabio Collini
 
Android Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeAndroid Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG Firenze
Fabio Collini
 
Testable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVMTestable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVM
Fabio Collini
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
Fabio Collini
 

More from Fabio Collini (20)

Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
 
Managing parallelism using coroutines
Managing parallelism using coroutinesManaging parallelism using coroutines
Managing parallelism using coroutines
 
Kotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community confKotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community conf
 
Kotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere StockholmKotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere Stockholm
 
Solid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon ItalySolid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon Italy
 
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila RomagnaSOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
 
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean ArchitectureSOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean Architecture
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFrom Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
 
Recap Google I/O 2018
Recap Google I/O 2018Recap Google I/O 2018
Recap Google I/O 2018
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+k
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UK
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJava
 
Android Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUKAndroid Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUK
 
Data Binding in Action using MVVM pattern
Data Binding in Action using MVVM patternData Binding in Action using MVVM pattern
Data Binding in Action using MVVM pattern
 
Android Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeAndroid Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG Firenze
 
Testable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVMTestable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVM
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
 

Recently uploaded

May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
Adele Miller
 
AI Genie Review: World’s First Open AI WordPress Website Creator
AI Genie Review: World’s First Open AI WordPress Website CreatorAI Genie Review: World’s First Open AI WordPress Website Creator
AI Genie Review: World’s First Open AI WordPress Website Creator
Google
 
A Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of PassageA Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of Passage
Philip Schwarz
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
timtebeek1
 
Artificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension FunctionsArtificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension Functions
Octavian Nadolu
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
Empowering Growth with Best Software Development Company in Noida - Deuglo
Empowering Growth with Best Software  Development Company in Noida - DeugloEmpowering Growth with Best Software  Development Company in Noida - Deuglo
Empowering Growth with Best Software Development Company in Noida - Deuglo
Deuglo Infosystem Pvt Ltd
 
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Mind IT Systems
 
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Łukasz Chruściel
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
TheSMSPoint
 
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
Alina Yurenko
 
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptx
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptxTop Features to Include in Your Winzo Clone App for Business Growth (4).pptx
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptx
rickgrimesss22
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Crescat
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOMLORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
lorraineandreiamcidl
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
Paco van Beckhoven
 
E-commerce Application Development Company.pdf
E-commerce Application Development Company.pdfE-commerce Application Development Company.pdf
E-commerce Application Development Company.pdf
Hornet Dynamics
 
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
Łukasz Chruściel
 
openEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain SecurityopenEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain Security
Shane Coughlan
 
Vitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdfVitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
Fermin Galan
 

Recently uploaded (20)

May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
 
AI Genie Review: World’s First Open AI WordPress Website Creator
AI Genie Review: World’s First Open AI WordPress Website CreatorAI Genie Review: World’s First Open AI WordPress Website Creator
AI Genie Review: World’s First Open AI WordPress Website Creator
 
A Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of PassageA Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of Passage
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
 
Artificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension FunctionsArtificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension Functions
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
Empowering Growth with Best Software Development Company in Noida - Deuglo
Empowering Growth with Best Software  Development Company in Noida - DeugloEmpowering Growth with Best Software  Development Company in Noida - Deuglo
Empowering Growth with Best Software Development Company in Noida - Deuglo
 
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
 
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
 
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
 
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptx
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptxTop Features to Include in Your Winzo Clone App for Business Growth (4).pptx
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptx
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOMLORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
 
E-commerce Application Development Company.pdf
E-commerce Application Development Company.pdfE-commerce Application Development Company.pdf
E-commerce Application Development Company.pdf
 
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
 
openEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain SecurityopenEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain Security
 
Vitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdfVitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdf
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
 

Using Dagger in a Clean Architecture project

  • 1. Using Dagger in a Clean Architecture project Fabio Collini
  • 7. 1. Speeds up builds 2. Enable on demand delivery 3. Simplify development 4. Reuse modules across apps 5. Experiment with new technologies 6. Scale development teams 7. Enables refactoring 8. Simplifies test automation Modularization - Why you should care https://jeroenmols.com/blog/2019/03/06/modularizationwhy/
  • 11. Main app BUILD SUCCESSFUL in 1m 12s 847 actionable tasks: 266 executed, 542 from cache, 39 up-to-date Feature app BUILD SUCCESSFUL in 43s 504 actionable tasks: 159 executed, 321 from cache, 24 up-to-date Feature module BUILD SUCCESSFUL in 30s 427 actionable tasks: 87 executed, 308 from cache, 32 up-to-date Core module BUILD SUCCESSFUL in 22s 308 actionable tasks: 63 executed, 220 from cache, 25 up-to-date Utils BUILD SUCCESSFUL in 6s 22 actionable tasks: 6 executed, 13 from cache, 3 up-to-date Poorman'sbenchmark
  • 12. Main app BUILD SUCCESSFUL in 1m 12s 847 actionable tasks: 266 executed, 542 from cache, 39 up-to-date Feature app BUILD SUCCESSFUL in 43s 504 actionable tasks: 159 executed, 321 from cache, 24 up-to-date Feature module BUILD SUCCESSFUL in 30s 427 actionable tasks: 87 executed, 308 from cache, 32 up-to-date Core module BUILD SUCCESSFUL in 22s 308 actionable tasks: 63 executed, 220 from cache, 25 up-to-date Utils BUILD SUCCESSFUL in 6s 22 actionable tasks: 6 executed, 13 from cache, 3 up-to-date Poorman'sbenchmark
  • 30. DaggerTip#1 @Component interface Component1 @Component(dependencies = [Component1::class]) interface Component2 fun main() { DaggerComponent2.builder() .build() }1
  • 31. DaggerTip#1 Exception in thread "main" java.lang.IllegalStateException: Component1 must be set at dagger.internal.Preconditions.checkBuilderRequirement(Preconditions.java:95) at DaggerComponent2$Builder.build(DaggerComponent2.java:35) @Component interface Component1 @Component(dependencies = [Component1::class]) interface Component2 fun main() { DaggerComponent2.builder() .build() }1
  • 32. DaggerTip#1 Exception in thread "main" java.lang.IllegalStateException: Component1 must be set at dagger.internal.Preconditions.checkBuilderRequirement(Preconditions.java:95) at DaggerComponent2$Builder.build(DaggerComponent2.java:35) @Component interface Component1 @Component(dependencies = [Component1::class]) interface Component2 fun main() { DaggerComponent2.builder() .component1(DaggerComponent1.create()) .build() }1
  • 33. DaggerTip#1 @Component interface Component1 @Component(dependencies = [Component1::class]) interface Component2 { @Component.Factory interface Factory { fun create(component1: Component1): Component2 }2 }3 fun main() { DaggerComponent2.factory() .create(DaggerComponent1.create()) }1
  • 34. DaggerTip#2 @Module class Module1 { @Provides fun pattern() = "#,###.00" @Provides fun format(pattern: String) = DecimalFormat(pattern) }1 Module1 pattern format
  • 35. DaggerTip#2 @Module class Module1 { @Provides fun pattern() = "#,###.00" @Provides fun format(pattern: String) = DecimalFormat(pattern) }1 @Component(modules = [Module1::class]) interface Component1 { val format: DecimalFormat }2 Module1 Component1 pattern format format
  • 36. DaggerTip#2 @Module class Module1 { @Provides fun pattern() = "#,###.00" @Provides fun format(pattern: String) = DecimalFormat(pattern) }1 @Component(modules = [Module1::class]) interface Component1 { val format: DecimalFormat }2 @Module class Module2 { }3 @Component( modules = [Module2::class], dependencies = [Component1::class] )5 interface Component2 { }4 Module2 Component2 Module1 Component1 pattern format format
  • 37. DaggerTip#2 @Module class Module1 { @Provides fun pattern() = "#,###.00" }1 @Component(modules = [Module1::class]) interface Component1 { }2 @Module class Module2 { @Provides fun format(pattern: String) = DecimalFormat(pattern) }3 @Component( modules = [Module2::class], dependencies = [Component1::class] )5 interface Component2 { val format: DecimalFormat }4 Module2 Component2 format Module1 Component1 pattern format error: [Dagger/MissingBinding] String cannot be provided without an @Inject constructor or an @Provides-annotated method. public abstract interface Component2 { ^ String is injected at Module2.format(pattern) DecimalFormat is provided at Component2.getFormat()
  • 38. DaggerTip#2 @Module class Module1 { @Provides fun pattern() = "#,###.00" }1 @Component(modules = [Module1::class]) interface Component1 { val pattern: String }2 @Module class Module2 { @Provides fun format(pattern: String) = DecimalFormat(pattern) }3 @Component( modules = [Module2::class], dependencies = [Component1::class] )5 interface Component2 { val format: DecimalFormat }4 Module2 Component2 format Module1 Component1 patternpattern format
  • 39. DaggerTip#2 @Module class Module1 { @Provides fun pattern() = "#,###.00" }1 @Component(modules = [Module1::class]) interface Component1 { val pattern: String }2 @Module class Module2 { @Provides fun format(pattern: String) = DecimalFormat(pattern) }3 @Component( modules = [Module2::class], dependencies = [Component1::class] )5 interface Component2 { val format: DecimalFormat }4 Module2 Component2 format Module1 Component1 patternpattern format
  • 40. DaggerTip#3 interface Component1 { val pattern: String }2 @Module class Module2 { @Provides fun format(pattern: String) = DecimalFormat(pattern) }3 @Component( modules = [Module2::class], dependencies = [Component1::class] )5 interface Component2 { val format: DecimalFormat }4
  • 41. DaggerTip#3 interface Component1 { val pattern: String }2 @Module class Module2 { @Provides fun format(pattern: String) = DecimalFormat(pattern) }3 @Component( modules = [Module2::class], dependencies = [Component1::class] )5 interface Component2 { val format: DecimalFormat }4 fun main() { val component1 = object : Component1 { override val pattern ="#,###.0000" }3 val component2 = DaggerComponent2.factory().create(component1) }4
  • 42. @Module class LibModule { @Provides fun provideMyLibObject() = MyLibObject() }3 @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 Lib
  • 43. @Module class LibModule { @Provides fun provideMyLibObject() = MyLibObject() }3 @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 Lib feature2 Lib feature1
  • 44. feature2 Lib feature1 @Component(dependencies = [LibComponent::class]) interface Feature1Component @Component(dependencies = [LibComponent::class]) interface Feature2Component @Module class LibModule { @Provides fun provideMyLibObject() = MyLibObject() }3 @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 feat1feat2Lib
  • 45. feature2 Lib feature1 @Singleton @Component(dependencies = [LibComponent::class]) interface Feature1Component @Singleton @Component(dependencies = [LibComponent::class]) interface Feature2Component @Module class LibModule { @Singleton @Provides fun provideMyLibObject() = MyLibObject() }3 @Singleton @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 Libfeat1feat2
  • 46. feature2 Lib feature1 @Scope annotation class Feature1SingletonScope @Feature1SingletonScope @Component(dependencies = [LibComponent::class]) interface Feature1Component @Scope annotation class Feature2SingletonScope @Feature2SingletonScope @Component(dependencies = [LibComponent::class]) interface Feature2Component @Scope annotation class LibSingletonScope @Module class LibModule { @LibSingletonScope @Provides fun provideMyLibObject() = MyLibObject() }3 @LibSingletonScope @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 Libfeat1feat2
  • 47. DaggerTip#4 @Module class LibModule { @Singleton @Provides fun provideMyLibObject() = MyLibObject() }3 @Singleton @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1
  • 48. DaggerTip#4 @Module class LibModule { @Singleton @Provides fun provideMyLibObject() = MyLibObject() }3 @Singleton @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 fun main() { val obj1 = DaggerLibComponent.create().myLibObject val obj2 = DaggerLibComponent.create().myLibObject println(obj1 === obj2) //false :( }2
  • 49. DaggerTip#4 @Module class LibModule { @Singleton @Provides fun provideMyLibObject() = MyLibObject() }3 @Singleton @Component(modules = [LibModule::class]) interface LibComponent { val myLibObject: MyLibObject }1 fun main() { val component = DaggerLibComponent.create() val obj1 = component.myLibObject val obj2 = component.myLibObject println(obj1 === obj2) //true :) }2
  • 50. interface LibComponentProvider { val libComponent: LibComponent }8 Lib
  • 51. interface Feature1Provider { val feature1Component: Feature1Component }7 interface LibComponentProvider { val libComponent: LibComponent }8 feature1Lib
  • 52. class MyApp : Application(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }6 interface Feature1Provider { val feature1Component: Feature1Component }7 interface LibComponentProvider { val libComponent: LibComponent }8 feature1LibApp
  • 53. class MyApp : Application(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }6 interface Feature1Provider { val feature1Component: Feature1Component }7 fun Application.feature1Component() = (this as Feature1Provider).feature1Component interface LibComponentProvider { val libComponent: LibComponent }8 fun Application.libComponent() = (this as LibComponentProvider).libComponent Libfeature1App
  • 54. class MyApp : Application(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }6 class MyFeature1App : Application(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }3 interface Feature1Provider { val feature1Component: Feature1Component }7 fun Application.feature1Component() = (this as Feature1Provider).feature1Component interface LibComponentProvider { val libComponent: LibComponent }8 fun Application.libComponent() = (this as LibComponentProvider).libComponent Libfeature1Feature1AppApp
  • 55. interface ComponentHolder { val map: MutableMap<KClass<*>, Any> }1 open class ComponentHolderApp : Application(), ComponentHolder { override val map = mutableMapOf<KClass<*>, Any>() }2 inline fun <reified C : Any> ComponentHolder.getOrCreate(factory: () -> C): C = map.getOrPut(C::class, factory) as C A “real” implementation is available here: https://github.com/fabioCollini/CleanWeather/blob/dagger/kotlinUtils/ src/main/java/it/codingjam/cleanweather/kotlinutils/ComponentHolder.kt
  • 56. class MyApp : Application(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }6 class MyFeature1App : Application(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }3 interface Feature1Provider { val feature1Component: Feature1Component }7 fun Application.feature1Component() = (this as Feature1Provider).feature1Component interface LibComponentProvider { val libComponent: LibComponent }8 fun Application.libComponent() = (this as LibComponentProvider).libComponent Libfeature1Feature1AppApp
  • 57. class MyApp : ComponentHolderApp(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }6 class MyFeature1App : ComponentHolderApp(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }3 interface Feature1Provider { val feature1Component: Feature1Component }7 fun ComponentHolderApp.feature1Component() = (this as Feature1Provider).feature1Component interface LibComponentProvider { val libComponent: LibComponent }8 fun ComponentHolderApp.libComponent() = (this as LibComponentProvider).libComponent Libfeature1Feature1AppApp
  • 58. class MyApp : ComponentHolderApp(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }6 class MyFeature1App : ComponentHolderApp(), LibComponentProvider, Feature1Provider { override val libComponent = DaggerLibComponent.create() override val feature1Component = DaggerFeature1Component.factory().create(libComponent) }3 interface Feature1Provider { val feature1Component: Feature1Component }7 fun ComponentHolderApp.feature1Component() = getOrCreate { }1 interface LibComponentProvider { val libComponent: LibComponent }8 fun ComponentHolderApp.libComponent() = getOrCreate { }2 Libfeature1Feature1AppApp
  • 59. class MyApp : ComponentHolderApp(), LibComponentProvider, Feature1Provider class MyFeature1App : ComponentHolderApp(), LibComponentProvider, Feature1Provider interface Feature1Provider { val feature1Component: Feature1Component }7 fun ComponentHolderApp.feature1Component() = getOrCreate { DaggerFeature1Component.factory() .create(libComponent()) }1 interface LibComponentProvider { val libComponent: LibComponent }8 fun ComponentHolderApp.libComponent() = getOrCreate { DaggerLibComponent.create() }2 Feature1App App Libfeature1
  • 60. class MyApp : ComponentHolderApp() class MyFeature1App : ComponentHolderApp() fun ComponentHolderApp.feature1Component() = getOrCreate { DaggerFeature1Component.factory() .create(libComponent()) }1 fun ComponentHolderApp.libComponent() = getOrCreate { DaggerLibComponent.create() }2 Libfeature1 Feature1App App
  • 61. @Feature1SingletonScope @Component(dependencies = [Lib1Component::class, Lib2Component::class]) interface Feature1Component { //... } DaggerTip#5 error: @Feature1SingletonScope Feature1Component depends on more than one scoped component: @dagger.Component(dependencies = {Lib1Component.class, Lib2Component.class}) ^ @Lib1SingletonScope Lib1Component @Lib2SingletonScope Lib2Component
  • 62. @Lib1SingletonScope @Component(modules = [Lib1Module::class]) interface Lib1Component { val myLibObject: MyLibObject }2 DaggerTip#5
  • 63. interface Lib1Component { val myLibObject: MyLibObject }2 @Lib1SingletonScope @Component(modules = [Lib1Module::class]) interface Lib1ComponentImpl : Lib1Component DaggerTip#5
  • 64. @Scope annotation class Lib1SingletonScope @Module class Lib1Module { @Lib1SingletonScope @Provides fun provideMyLibObject() = MyLibObject() }1 interface Lib1Component { val myLibObject: MyLibObject }2 @Lib1SingletonScope @Component(modules = [Lib1Module::class]) interface Lib1ComponentImpl : Lib1Component fun ComponentHolderApp.lib1Component(): Lib1Component = getOrCreate { DaggerLib1ComponentImpl.create() } DaggerTip#5
  • 65. @Scope internal annotation class Lib1SingletonScope @Module internal class Lib1Module { @Lib1SingletonScope @Provides fun provideMyLibObject() = MyLibObject() }1 interface Lib1Component { val myLibObject: MyLibObject }2 @Lib1SingletonScope @Component(modules = [Lib1Module::class]) internal interface Lib1ComponentImpl : Lib1Component fun ComponentHolderApp.lib1Component(): Lib1Component = getOrCreate { DaggerLib1ComponentImpl.create() } DaggerTip#5
  • 66. DaggerTip#5 interface Lib1Component { val myLibObject: MyLibObject }2 fun ComponentHolderApp.lib1Component(): Lib1Component = getOrCreate { DaggerLib1ComponentImpl.create() }
  • 67. NODaggerTip interface Lib1Component { val myLibObject: MyLibObject }2 fun ComponentHolderApp.lib1Component(): Lib1Component = getOrCreate { DaggerLib1ComponentImpl.create() }
  • 68. NODaggerTip interface Lib1Component { val myLibObject: MyLibObject }2 private class Lib1ComponentImpl : Lib1Component { override val myLibObject by lazy { MyLibObject() }3 }4 fun ComponentHolderApp.lib1Component(): Lib1Component = getOrCreate { Lib1ComponentImpl() }
  • 71. class UseCase @Inject constructor( val repository: Repository) { //... }9 domain domain repository Repository RepositoryImpl UseCase
  • 72. class UseCase @Inject constructor( val repository: Repository) { //... }9 @Component interface DomainComponent { val useCase: UseCase }2 domain domain repository Repository RepositoryImpl UseCase Domain Component
  • 73. class UseCase @Inject constructor( val repository: Repository) { //... }9 @Module class DomainModule { @Provides fun provideRepository(): Repository = RepositoryImpl() }1 @Component(modules = [DomainModule::class]) interface DomainComponent { val useCase: UseCase }2 domain domain repository Repository RepositoryImpl UseCase Domain Component
  • 74. @Component interface DomainComponent { val useCase: UseCase }2 domain domain repository Repository RepositoryImpl UseCase Domain Component
  • 75. interface DomainDependencies { val repository: Repository }3 @Component( dependencies = [DomainDependencies::class] )9 interface DomainComponent { val useCase: UseCase }2 domain domain repository Repository RepositoryImpl UseCase Domain Dependencies Domain Component
  • 76. interface DomainDependencies { val repository: Repository }3 @Component( dependencies = [DomainDependencies::class] )9 interface DomainComponent { val useCase: UseCase }2 @Component interface RepositoryComponent : DomainDependencies domainrepo domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component
  • 77. fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( ??? )3 }4 domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component domain
  • 78. fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( map[DomainDependencies::class] as DomainDependencies )3 }4 domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component domain
  • 79. class MyApp : ComponentHolderApp() { override fun onCreate() { map[DomainDependencies::class] = DaggerRepositoryComponent.create() }1 }21 fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( map[DomainDependencies::class] as DomainDependencies )3 }4 domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component domainApp
  • 80.
  • 81. interface DomainDependencies { val repository: Repository }2 @Component(dependencies = [DomainDependencies::class]) interface DomainComponent { val useCase: UseCase }3 fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( ??? )4 }5 @Component interface RepositoryComponent : DomainDependencies domainrepo domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component
  • 82. interface DomainDependencies { val repository: Repository }2 @get:InversionDef val ComponentHolderApp.domainDependencies by Inversion.of(DomainDependencies::class) @Component(dependencies = [DomainDependencies::class]) interface DomainComponent { val useCase: UseCase }3 fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( ??? )4 }5 @Component interface RepositoryComponent : DomainDependencies domainrepo domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component
  • 83. interface DomainDependencies { val repository: Repository }2 @get:InversionDef val ComponentHolderApp.domainDependencies by Inversion.of(DomainDependencies::class) @Component(dependencies = [DomainDependencies::class]) interface DomainComponent { val useCase: UseCase }3 fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( domainDependencies() )4 }5 @Component interface RepositoryComponent : DomainDependencies domainrepo domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component
  • 84. interface DomainDependencies { val repository: Repository }2 @get:InversionDef val ComponentHolderApp.domainDependencies by Inversion.of(DomainDependencies::class) @Component(dependencies = [DomainDependencies::class]) interface DomainComponent { val useCase: UseCase }3 fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( domainDependencies() )4 }5 @Component interface RepositoryComponent : DomainDependencies @InversionProvider fun ComponentHolderApp.provideDomainDependencies(): DomainDependencies = getOrCreate { DaggerRepositoryComponent.create() }6 domainrepo domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component
  • 85. interface DomainDependencies { val repository: Repository }2 @get:InversionDef val ComponentHolderApp.domainDependencies by Inversion.of(DomainDependencies::class) @Component(dependencies = [DomainDependencies::class]) interface DomainComponent { val useCase: UseCase }3 fun ComponentHolderApp.domainComponent() = getOrCreate { DaggerDomainComponent.factory().create( domainDependencies() )4 }5 @Component interface RepositoryComponent : DomainDependencies @InversionProvider fun ComponentHolderApp.provideDomainDependencies(): DomainDependencies = getOrCreate { DaggerRepositoryComponent.create() }6 domainrepo domain repository Repository RepositoryImpl UseCase Repository Component Domain Dependencies Domain Component
  • 87. class MainActivity : AppCompatActivity() { @Inject lateinit var useCase: UseCase override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val activityComponent = DaggerMainActivityComponent.factory().create( (application as ComponentHolderApp).domainComponent(), this )1 activityComponent.inject(this) setContentView(R.layout.activity_main) findViewById<TextView>(R.id.text).text = useCase.retrieve() }2 }3
  • 88. class MainActivity : AppCompatActivity() { @Inject lateinit var useCase: UseCase override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val activityComponent = DaggerMainActivityComponent.factory().create( (application as ComponentHolderApp).domainComponent(), this )1 activityComponent.inject(this) setContentView(R.layout.activity_main) findViewById<TextView>(R.id.text).text = useCase.retrieve() }2 }3
  • 89. class MainActivity : AppCompatActivity() { @Inject lateinit var useCase: UseCase override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val activityComponent = DaggerMainActivityComponent.factory().create( (application as ComponentHolderApp).domainComponent(), this )1 activityComponent.inject(this) setContentView(R.layout.activity_main) findViewById<TextView>(R.id.text).text = useCase.retrieve() }2 }3
  • 90. class MainActivityTest { @get:Rule val rule = ActivityTestRule(MainActivity::class.java, false, false) private val useCaseMock = mock<UseCase>() }4
  • 91. class MainActivityTest { @get:Rule val rule = ActivityTestRule(MainActivity::class.java, false, false) private val useCaseMock = mock<UseCase>() @Before fun setUp() { }2 }4
  • 92. class MainActivityTest { @get:Rule val rule = ActivityTestRule(MainActivity::class.java, false, false) private val useCaseMock = mock<UseCase>() @Before fun setUp() { val app = ApplicationProvider.getApplicationContext<ComponentHolderApp>() app.map.clear() app.map[DomainComponent::class] = object : DomainComponent { override val useCase = useCaseMock }1 }2 }4
  • 93. class MainActivityTest { @get:Rule val rule = ActivityTestRule(MainActivity::class.java, false, false) private val useCaseMock = mock<UseCase>() @Before fun setUp() { val app = ApplicationProvider.getApplicationContext<ComponentHolderApp>() app.map.clear() app.map[DomainComponent::class] = object : DomainComponent { override val useCase = useCaseMock }1 }2 @Test fun launchActivity() { whenever(useCaseMock.retrieve()) doReturn "ABCDEF" rule.launchActivity(null) onView(withId(R.id.text)) .check(matches(withText("ABCDEF"))) }3 }4
  • 94. class MainActivityTest { @get:Rule val rule = ActivityTestRule(MainActivity::class.java, false, false) private val useCaseMock = mock<UseCase>() @Before fun setUp() { val app = ApplicationProvider.getApplicationContext<ComponentHolderApp>() app.map.clear() app.map[DomainComponent::class] = object : DomainComponent { override val useCase = useCaseMock }1 }2 @Test fun launchActivity() { whenever(useCaseMock.retrieve()) doReturn "ABCDEF" rule.launchActivity(null) onView(withId(R.id.text)) .check(matches(withText("ABCDEF"))) }3 }4
  • 95. Wrappingup Component dependencies Each module exposes: a Component interface a method to create the Component A map in the App can be useful to: manage singletons replace real objects with fakes/mocks
  • 96. Links Demo Project github.com/fabioCollini/CleanWeather/tree/dagger Droidcon Italy talk - SOLID principles in practice: the Clean Architecture youtube.com/watch?v=GlDsfq3xHvo&t= Implementing Dependency Inversion using Dagger components medium.com/google-developer-experts/implementing-dependency-inversion-using-dagger-components-d6b0fb3b6b5e Inversion library github.com/fabioCollini/Inversion Yigit Boyar, Florina Muntenescu - Build a Modular Android App Architecture (Google I/O'19) youtube.com/watch?v=PZBg5DIzNww Jeroen Mols - Modularization - Why you should care jeroenmols.com/blog/2019/03/06/modularizationwhy/ Ben Weiss - Dependency injection in a multi module project medium.com/androiddevelopers/dependency-injection-in-a-multi-module-project-1a09511c14b7 Marcos Holgado - Using Dagger in a multi-module project proandroiddev.com/using-dagger-in-a-multi-module-project-1e6af8f06ffc