Pro Yearly is on sale from $80 to $50! »

Scaling Dagger: DI in a modular world

575f7b9c8722a6b4fec3d47c69d473c9?s=47 Nacho L.
December 20, 2019

Scaling Dagger: DI in a modular world

As your Android app grows in size, using a DI framework like Dagger becomes a necessity. But as your app keeps growing and aging, does Dagger scale well with it? Does it play well with modularized apps? How do you visualize and navigate the graph as complexity grows?

In this session we will describe the mechanisms that we use at Twitter to facilitate dependency injection, review the pain points we encountered with Dagger, and share some practical advice on how to address them. Finally, we’ll offer a sneak peek of the tools that built on Dagger to facilitate DI that we will open source in 2020.

575f7b9c8722a6b4fec3d47c69d473c9?s=128

Nacho L.

December 20, 2019
Tweet

Transcript

  1. Scaling Dagger DI in a modular world Droidcon Madrid 2019

  2. @CesarDielo César Puerta Technical Lead Twitter for Android @mrmans0n Nacho

    López Software Engineer Android Foundation Architecture
  3. None
  4. None
  5. Author: Ben Sandofsky Date: Mon Jun 21 23:24:55 2010 -0700

    Initial import.
  6. Author: Ben Sandofsky Date: Mon Jun 21 23:24:55 2010 -0700

    Initial import.
  7. None
  8. None
  9. None
  10. None
  11. None
  12. None
  13. None
  14. None
  15. Periscope @PeriscopeCo You might have heard some news: It involves

    a blue bird. #YouCanGuessTheRest #WeJoinedTheFlockInJanuary 13 Mar 2015 629 761
  16. +

  17. None
  18. None
  19. None
  20. None
  21. D D

  22. DI is important

  23. DI is important Reusability for…

  24. DI is important Testability for…

  25. DI is important Correctness for…

  26. DI is important Refactoring for…

  27. regardless of app size DI is important

  28. None
  29. None
  30. Static checks

  31. Static checks Errors at compile time > Runtime errors

  32. Description: [Dagger/MissingBinding] com.twitter.ui.util.UiArchLayoutContentViewProvider cannot be provided without an @Inject constructor

    or an @Provides-annotated method. Description: [Dagger/DuplicateBindings] com.twitter.ui.util.UiArchLayoutContentViewProvider is bound multiple times.
  33. Code generation

  34. Code generation Automating the creation of glue code

  35. val inflater = LayoutInflater.from(context) val renderer = Renderer(inflater) val database

    = Database() val timeline = Timeline(database, renderer) class Renderer (database: Database, renderer: Renderer) class Timeline class Database() (inflater: LayoutInflater)
  36. class Renderer (database: Database, renderer: Renderer) class Timeline class Database

    () (inflater: LayoutInflater) @Inject constructor @Inject constructor @Inject constructor
  37. Graph Architecture

  38. Graph Architecture Scoped object graphs mapping to the app lifecycle

  39. Graph Architecture Scoped object graphs mapping to the app lifecycle

    @Component @Subcomponent
  40. None
  41. None
  42. None
  43. None
  44. @ApplicationScope @UserScope @RetainedScope @ViewScope

  45. @Component(modules = [ApplicationModule::class]) @ApplicationScope interface ApplicationObjectGraph

  46. @Component(modules = [ApplicationModule::class]) @ApplicationScope interface ApplicationObjectGraph { fun userObjectGraphBuilder(): UserObjectGraph.Builder

    }
  47. @Subcomponent(modules = [UserModule::class]) @UserScope interface UserObjectGraph @Component(modules = [ApplicationModule::class]) @ApplicationScope

    interface ApplicationObjectGraph { fun userObjectGraphBuilder(): UserObjectGraph.Builder }
  48. { fun featureRetainedBuilder(): FeatureRetainedObjectGraph.Builder } @Subcomponent(modules = [UserModule::class]) @UserScope interface

    UserObjectGraph @Component(modules = [ApplicationModule::class]) @ApplicationScope interface ApplicationObjectGraph { fun userObjectGraphBuilder(): UserObjectGraph.Builder }
  49. { fun featureRetainedBuilder(): FeatureRetainedObjectGraph.Builder } @Subcomponent(modules = [UserModule::class]) @UserScope interface

    UserObjectGraph @ApplicationScope interface ApplicationObjectGraph { fun userObjectGraphBuilder(): UserObjectGraph.Builder } @Subcomponent(modules = [FeatureRetainedModule::class]) @RetainedScope interface FeatureRetainedObjectGraph
  50. { fun featureRetainedBuilder(): FeatureRetainedObjectGraph.Builder } @Subcomponent(modules = [UserModule::class]) @UserScope interface

    UserObjectGraph @ApplicationScope interface ApplicationObjectGraph { fun userObjectGraphBuilder(): UserObjectGraph.Builder } @Subcomponent(modules = [FeatureRetainedModule::class]) @RetainedScope interface FeatureRetainedObjectGraph { fun featureViewGraphBuilder(): FeatureViewObjectGraph.Builder }
  51. { fun featureRetainedBuilder(): FeatureRetainedObjectGraph.Builder } @UserScope interface UserObjectGraph @Subcomponent(modules =

    [FeatureRetainedModule::class]) @RetainedScope interface FeatureRetainedObjectGraph { fun featureViewGraphBuilder(): FeatureViewObjectGraph.Builder } @Subcomponent(modules = [FeatureViewModule::class]) @ViewScope interface FeatureViewObjectGraph
  52. { fun featureRetainedBuilder(): FeatureRetainedObjectGraph.Builder } @UserScope interface UserObjectGraph Components or

    ? Subcomponents @ApplicationScope @UserScope @RetainedScope @ViewScope
  53. Components for the root graph for the nested graphs Subcomponents

    @ApplicationScope @UserScope @RetainedScope @ViewScope
  54. Where do we place our graphs?

  55. None
  56. @ApplicationScope @UserScope @RetainedScope @ViewScope

  57. Let’s compose a Tweet

  58. :features:composer @RetainedScope @ViewScope @ApplicationScope @UserScope

  59. :features:composer ComposerActivity ComposerViewModel ComposerViewDelegate @RetainedScope @ViewScope @ApplicationScope @UserScope

  60. :features:composer ComposerActivity ComposerViewModel ComposerViewDelegate @RetainedScope @ViewScope @ApplicationScope @UserScope

  61. :subsystems:tweets TweetRepository :features:composer @RetainedScope @ViewScope @ApplicationScope @UserScope

  62. :subsystems:tweets TweetRepository :features:composer @RetainedScope @ViewScope @ApplicationScope @UserScope

  63. :subsystems:tweets TweetRepository :features:composer @RetainedScope @ViewScope @ApplicationScope @UserScope

  64. :subsystems:tweets TweetRepository :features:composer @RetainedScope @ViewScope @ApplicationScope @UserScope

  65. :twitter:media ImageUploader :features:composer :subsystems:tweets @RetainedScope @ViewScope @UserScope @ApplicationScope

  66. :twitter:media ImageUploader :features:composer :subsystems:tweets @RetainedScope @ViewScope @ApplicationScope @UserScope

  67. :twitter:media ImageUploader :features:composer :subsystems:tweets @RetainedScope @ViewScope @ApplicationScope @UserScope

  68. :twitter:media ImageUploader :features:composer :subsystems:tweets @RetainedScope @ViewScope @ApplicationScope @UserScope

  69. :core:network OkHttpClient :features:composer :subsystems:tweets :twitter:media @RetainedScope @ViewScope @UserScope @ApplicationScope

  70. :core:network OkHttpClient :features:composer :subsystems:tweets :twitter:media @RetainedScope @ViewScope @ApplicationScope @UserScope

  71. :core:network :features:composer :subsystems:tweets :twitter:media @RetainedScope @ViewScope @ApplicationScope @UserScope ComposerNotifHandler

  72. :core:network :features:composer :subsystems:tweets :twitter:media @RetainedScope @ViewScope @ApplicationScope @UserScope ComposerNotifHandler

  73. :core:network :features:composer :subsystems:tweets :twitter:media @RetainedScope @ViewScope @ApplicationScope @UserScope M M

    M M M M
  74. :core:network :features:composer :subsystems:tweets :twitter:media @RetainedScope @ViewScope @ApplicationScope @UserScope M M

    M M M M
  75. Ideal Location

  76. App wide graphs definitions belong in the top gradle module.

    Ideal Location
  77. Graphs can be large!

  78. @Component @ApplicationScope interface ApplicationObjectGraph } fun composerLauncher(): ComposerLauncher fun composerNotificationHandler():

    ComposerNotifHandler { ]) (modules = [ComposerAppModule::class
  79. @Component @ApplicationScope interface ApplicationObjectGraph } fun composerLauncher(): ComposerLauncher fun composerNotificationHandler():

    ComposerNotifHandler fun tweetsRepository(): TweetsRepository fun tweetsDataSource(): TweetsDataSource fun imageLoader(): ImageLoader { , TweetRepositoryAppModule::cl (modules = [ComposerAppModule::class
  80. fun okHttpClient(): OkHttpClient fun twitterApiService(): TwitterApiService @Component @ApplicationScope interface ApplicationObjectGraph

    fun composerLauncher(): ComposerLauncher fun composerNotificationHandler(): ComposerNotifHandler fun tweetsRepository(): TweetsRepository fun tweetsDataSource(): TweetsDataSource fun imageLoader(): ImageLoader { fun loginController(): LoginController fun loginAssistController(): LoginAssistController fun appEventTracker(): AppEventTracker fun installationReferrer(): InstallationReferrer fun jobManager(): JobManager fun playServicesUtil(): PlayServicesUtil fun httpRequestController(): HttpRequestController fun registrableHeadsetPlugReceiver(): RegistrableHeadsetPlugReceiver , TweetRepositoryAppModule::cl (modules = [ComposerAppModule::class
  81. fun okHttpClient(): OkHttpClient fun twitterApiService(): TwitterApiService @Component @ApplicationScope interface ApplicationObjectGraph

    fun composerLauncher(): ComposerLauncher fun composerNotificationHandler(): ComposerNotifHandler fun tweetsRepository(): TweetsRepository fun tweetsDataSource(): TweetsDataSource fun imageLoader(): ImageLoader { fun loginController(): LoginController fun loginAssistController(): LoginAssistController fun appEventTracker(): AppEventTracker fun installationReferrer(): InstallationReferrer fun jobManager(): JobManager fun playServicesUtil(): PlayServicesUtil fun httpRequestController(): HttpRequestController fun registrableHeadsetPlugReceiver(): RegistrableHeadsetPlugReceiver , TweetRepositoryAppModule::cl (modules = [ComposerAppModule::class
  82. fun okHttpClient(): OkHttpClient fun twitterApiService(): TwitterApiService @Component @ApplicationScope interface ApplicationObjectGraph

    fun composerLauncher(): ComposerLauncher fun composerNotificationHandler(): ComposerNotifHandler fun tweetsRepository(): TweetsRepository fun tweetsDataSource(): TweetsDataSource fun imageLoader(): ImageLoader { fun loginController(): LoginController fun loginAssistController(): LoginAssistController fun appEventTracker(): AppEventTracker fun installationReferrer(): InstallationReferrer fun jobManager(): JobManager fun playServicesUtil(): PlayServicesUtil fun httpRequestController(): HttpRequestController fun registrableHeadsetPlugReceiver(): RegistrableHeadsetPlugReceiver , TweetRepositoryAppModule::cl (modules = [ComposerAppModule::class
  83. Subgraphs

  84. Subgraphs Slices of an Object Graph

  85. @Component @ApplicationScope interface ApplicationObjectGraph { } fun okHttpClient(): OkHttpClient fun

    twitterApiService(): TwitterApiService fun composerLauncher(): ComposerLauncher fun composerNotificationHandler(): ComposerNotifHandler fun tweetsRepository(): TweetsRepository fun tweetsDataSource(): TweetsDataSource fun imageLoader(): ImageLoader :app:twitter
  86. : @Component @ApplicationScope interface ApplicationObjectGraph { } fun okHttpClient(): OkHttpClient

    fun twitterApiService(): TwitterApiService fun composerLauncher(): ComposerLauncher fun composerNotificationHandler(): ComposerNotifHandler fun tweetsRepository(): TweetsRepository fun tweetsDataSource(): TweetsDataSource fun imageLoader(): ImageLoader ComposerAppSubgraph interface ComposerAppSubgraph { } :app:twitter :features:composer
  87. : @Component @ApplicationScope interface ApplicationObjectGraph { } fun okHttpClient(): OkHttpClient

    fun twitterApiService(): TwitterApiService fun tweetsRepository(): TweetsRepository fun tweetsDataSource(): TweetsDataSource fun imageLoader(): ImageLoader ComposerAppSubgraph :app:twitter
  88. @Component @ApplicationScope interface ApplicationObjectGraph , : { } fun okHttpClient():

    OkHttpClient fun twitterApiService(): TwitterApiService fun tweetsRepository(): TweetsRepository fun tweetsDataSource(): TweetsDataSource fun imageLoader(): ImageLoader ComposerAppSubgraph interface TweetRepositoryAppSubgraph { } TweetRepositoryAppSubgraph :app:twitter :subsystems:tweets
  89. @Component @ApplicationScope interface ApplicationObjectGraph , : { } fun okHttpClient():

    OkHttpClient fun twitterApiService(): TwitterApiService fun imageLoader(): ImageLoader ComposerAppSubgraph TweetRepositoryAppSubgraph :app:twitter
  90. @Component @ApplicationScope interface ApplicationObjectGraph , : { } fun okHttpClient():

    OkHttpClient fun twitterApiService(): TwitterApiService fun imageLoader(): ImageLoader ComposerAppSubgraph TweetRepositoryAppSubgraph interface MediaAppSubgraph { } MediaAppSubgraph , :app:twitter :twitter:media
  91. @Component @ApplicationScope interface ApplicationObjectGraph , : { } fun okHttpClient():

    OkHttpClient fun twitterApiService(): TwitterApiService ComposerAppSubgraph TweetRepositoryAppSubgraph MediaAppSubgraph , :app:twitter
  92. @Component @ApplicationScope interface ApplicationObjectGraph , : fun okHttpClient(): OkHttpClient fun

    twitterApiService(): TwitterApiService ComposerAppSubgraph TweetRepositoryAppSubgraph MediaAppSubgraph , interface CoreNetworkAppSubgraph { } CoreNetworkAppSubgraph , :core:network :app:twitter
  93. @Component @ApplicationScope interface ApplicationObjectGraph , : ComposerAppSubgraph TweetRepositoryAppSubgraph MediaAppSubgraph ,

    CoreNetworkAppSubgraph , :app:twitter ApplicationObjectGraph
  94. :core:network :features:composer :subsystems:tweets :twitter:media TweetRepositoryAppSubgraph MediaAppSubgraph CoreNetworkAppSubgraph ApplicationObjectGraph ComposerAppSubgraph

  95. :core:network :features:composer :subsystems:tweets :twitter:media TweetRepositoryAppSubgraph MediaAppSubgraph CoreNetworkAppSubgraph ApplicationObjectGraph ComposerAppSubgraph

  96. ApplicationObjectGraph UserObjectGraph Multiple Retained and ViewObjectGraphs

  97. ApplicationObjectGraph UserObjectGraph Multiple Retained and ViewObjectGraphs

  98. Big monolithic graphs

  99. Big monolithic graphs Scatter the definitions of the object graphs

    into subgraphs that can live in the gradle modules where the bindings are actually defined.
  100. How to instantiate your graphs?

  101. component = .application(this) .build() class MyApplication : Application { private

    lateinit var component: ApplicationObjectGraph override fun onCreate() { } super.onCreate() } DaggerApplicationObjectGraph.builder()
  102. component = .application(this) .build() class MyApplication : Application { private

    lateinit var component: ApplicationObjectGraph override fun onCreate() { } super.onCreate() } DaggerApplicationObjectGraph.builder()
  103. Dagger.getObjectGraphBuilder<ApplicationObjectGraph>() component = .application(this) .build() class MyApplication : Application {

    private lateinit var component: ApplicationObjectGraph override fun onCreate() { } super.onCreate() }
  104. fun <B : Any> getObjectGraphBuilder(builderClass: Class<B>): B } } class

    Dagger { companion object { inline fun <reified B : Any> getObjectGraphBuilder(): B = getObjectGraphBuilder(B::class.java) } {
  105. fun <B : Any> getObjectGraphBuilder(builderClass: Class<B>): B } } class

    Dagger { companion object { inline fun <reified B : Any> getObjectGraphBuilder(): B = getObjectGraphBuilder(B::class.java) } {
  106. if (!builderClass.isAnnotationPresent(Component.Builder::class.java)) { throw IllegalArgumentException("Not a component builder class: $builderClass")

    } val componentClass = builderClass.enclosingClass ?: throw IllegalStateException("Component builder is not enclosed in a component.") val generatedDaggerClassName = getDaggerComponentImplementationClassName(componentClass) val generatedObjectGraphClass = ReflectionUtils.loadClass<Any>(generatedDaggerClassName) ?: throw IllegalStateException("Generated Dagger class can't be found: $generatedDagge val builderMethod = ReflectionUtils.getMethod(generatedObjectGraphClass, "builder") ?: throw IllegalStateException("The Dagger component generated class does not contain return ReflectionUtils.invokeMethod<B>(builderMethod, null) ?: throw IllegalStateException("The builder() method returned null.") fun <B : Any> getObjectGraphBuilder(builderClass: Class<B>): B } } class Dagger { companion object { inline fun <reified B : Any> getObjectGraphBuilder(): B = getObjectGraphBuilder(B::class.java) } {
  107. if (!builderClass.isAnnotationPresent(Component.Builder::class.java)) { throw IllegalArgumentException("Not a component builder class: $builderClass")

    } val componentClass = builderClass.enclosingClass ?: throw IllegalStateException("Component builder is not enclosed in a component.") val generatedDaggerClassName = getDaggerComponentImplementationClassName(componentClass) val generatedObjectGraphClass = ReflectionUtils.loadClass<Any>(generatedDaggerClassName) ?: throw IllegalStateException("Generated Dagger class can't be found: $generatedDagge val builderMethod = ReflectionUtils.getMethod(generatedObjectGraphClass, "builder") ?: throw IllegalStateException("The Dagger component generated class does not contain return ReflectionUtils.invokeMethod<B>(builderMethod, null) ?: throw IllegalStateException("The builder() method returned null.") fun <B : Any> getObjectGraphBuilder(builderClass: Class<B>): B } } class Dagger { companion object { inline fun <reified B : Any> getObjectGraphBuilder(): B = getObjectGraphBuilder(B::class.java) } TL;DR Use reflection once to find and instantiate the graph’s Component.Builder {
  108. if (!builderClass.isAnnotationPresent(Component.Builder::class.java)) { throw IllegalArgumentException("Not a component builder class: $builderClass")

    } val componentClass = builderClass.enclosingClass ?: throw IllegalStateException("Component builder is not enclosed in a component.") val generatedDaggerClassName = getDaggerComponentImplementationClassName(componentClass) val generatedObjectGraphClass = ReflectionUtils.loadClass<Any>(generatedDaggerClassName) ?: throw IllegalStateException("Generated Dagger class can't be found: $generatedDagge val builderMethod = ReflectionUtils.getMethod(generatedObjectGraphClass, "builder") ?: throw IllegalStateException("The Dagger component generated class does not contain return ReflectionUtils.invokeMethod<B>(builderMethod, null) ?: throw IllegalStateException("The builder() method returned null.") fun <B : Any> getObjectGraphBuilder(builderClass: Class<B>): B } } class Dagger { companion object { inline fun <reified B : Any> getObjectGraphBuilder(): B = getObjectGraphBuilder(B::class.java) } Don’t worry! We will share the code in GitHub! {
  109. Referencing generated code DaggerApplicationObjectGraph.builder()

  110. Referencing generated code Use reflection once to grab the root

    object graph builder. Dagger.getObjectGraphBuilder<ApplicationObjectGraph>() DaggerApplicationObjectGraph.builder()
  111. How to access graphs at the top from intermediate layers?

  112. ApplicationObjectGraph

  113. ApplicationObjectGraph :twitter:media ImageUploader L LegacyActivity

  114. :twitter:media L ImageUploader LegacyActivity ApplicationObjectGraph

  115. :twitter:media MediaAppSubgraph L LegacyActivity ApplicationObjectGraph

  116. object ApplicationObjectGraphProvider { var graphProvider: Any? }

  117. component = Dagger.getObjectGraphBuilder<ApplicationObjectGraph>() .application(this) .build() class MyApplication : Application {

    private lateinit var component: ApplicationObjectGraph override fun onCreate() { } super.onCreate() }
  118. component = Dagger.getObjectGraphBuilder<ApplicationObjectGraph>() .application(this) .build() class MyApplication : Application {

    private lateinit var component: ApplicationObjectGraph override fun onCreate() { } super.onCreate() }
  119. super.onCreate() } ApplicationObjectGraphProvider.graphProvider = Dagger.getObjectGraphBuilder<ApplicationObjectGraph>() .application(this) .build() class MyApplication :

    Application { override fun onCreate() { }
  120. ApplicationObjectGraph ApplicationObjectGraphProvider

  121. ApplicationObjectGraph ApplicationObjectGraphProvider :twitter:media L ImageUploader LegacyActivity MediaAppSubgraph

  122. (ApplicationObjectGraphProvider.graphProvider as MediaAppSubgraph).imageUploader()

  123. object ApplicationObjectGraphProvider { } fun <T> getSubgraph(clazz: Class<T>): T var

    graphProvider: Any?
  124. object ApplicationObjectGraphProvider { } fun <T> getSubgraph(clazz: Class<T>): T var

    graphProvider: Any? { return clazz.cast(graphProvider) }
  125. fun imageUploader(): ImageUploader interface MediaAppSubgraph { }

  126. fun imageUploader(): ImageUploader interface MediaAppSubgraph { } companion object {

    fun get(): MediaAppSubgraph { return ApplicationObjectGraphProvider.graphProvider .getSubgraph(MediaAppSubgraph::class.java) }
  127. val uploader = MediaAppSubgraph.get().imageUploader() fun imageUploader(): ImageUploader interface MediaAppSubgraph {

    } companion object { fun get(): MediaAppSubgraph { return ApplicationObjectGraphProvider.graphProvider .getSubgraph(MediaAppSubgraph::class.java) }
  128. val uploader = MediaAppSubgraph.get().imageUploader()

  129. Grabbing objects from Dagger w/o visibility MediaAppSubgraph L LegacyActivity ApplicationObjectGraph

  130. Grabbing objects from Dagger w/o visibility Wrap your Application and

    User graphs in providers that can be statically obtained, and add a way to extract subgraphs from them. ApplicationObjectGraphProvider UserObjectGraphProvider MediaAppSubgraph L LegacyActivity
  131. How would that work in testing?

  132. object ApplicationObjectGraphProvider { } var graphProvider: Any? private val mapping:

    Map<Class<*>, Any> = mutableMapOf() fun <T> overrideSubgraph(clazz: Class<T>, subgraph: T) { mapping[clazz] = subgraph } fun <T> getSubgraph(clazz: Class<T>): T { if (mapping.containsKey(clazz)) return clazz.cast(mapping[clazz]) return clazz.cast(graphProvider) }
  133. object ApplicationObjectGraphProvider { } var graphProvider: Any? private val mapping:

    Map<Class<*>, Any> = mutableMapOf() fun <T> overrideSubgraph(clazz: Class<T>, subgraph: T) { mapping[clazz] = subgraph } fun <T> getSubgraph(clazz: Class<T>): T { if (mapping.containsKey(clazz)) return clazz.cast(mapping[clazz]) return clazz.cast(graphProvider) }
  134. How to decouple features?

  135. How to decouple features? The app navigation case

  136. None
  137. ComposerActivity MainActivity DMActivity TweetDetailActivity C D M TD

  138. ComposerActivity MainActivity DMActivity TweetDetailActivity C D M TD

  139. ComposerActivity MainActivity DMActivity TweetDetailActivity C D M TD

  140. ComposerActivity MainActivity DMActivity TweetDetailActivity C D M TD Circular Dependency!

  141. ComposerActivity MainActivity TweetDetailActivity C D M TD DMActivity :subsystems:navigation

  142. ComposerActivity MainActivity TweetDetailActivity C D M TD Intent().putExtra(…) DMActivity :subsystems:navigation

    C D M TD C D M TD Intent factories
  143. ComposerActivity MainActivity TweetDetailActivity C D M TD DMActivity :subsystems:navigation C

    D M TD Intent factories ActivityArgs
  144. ComposerActivity MainActivity TweetDetailActivity C D M TD DMActivity :subsystems:navigation TD

    M D C ActivityArgs
  145. ComposerActivity MainActivity TweetDetailActivity C D M TD DMActivity :subsystems:navigation TD

    M D C ActivityArgs
  146. interface ActivityArgs { fun toIntent(context: Context, clazz: Class<out Activity>): Intent

    }
  147. override fun toIntent(context: Context, clazz: Class<out Activity>): { .apply {

    putExtra("user_id", uid) putExtra("text", text) } } interface ActivityArgs { fun toIntent(context: Context, clazz: Class<out Activity>): Intent } data class ComposerArgs(val uid: Long, val text: String): ActivityArgs { } return Intent(context, clazz)
  148. override fun toIntent(context: Context, clazz: Class<out Activity>): { .apply {

    putExtra("user_id", uid) putExtra("text", text) } } interface ActivityArgs { fun toIntent(context: Context, clazz: Class<out Activity>): Intent } data class ComposerArgs(val uid: Long, val text: String): ActivityArgs { } return Intent(context, clazz)
  149. Service Discovery

  150. Service Discovery Multibindings

  151. C D TD ComposerArgs TweetDetailArgs DMArgs :features:tweetdetail :features:dm :features:composer @Multibinds

  152. C D TD ComposerArgs TweetDetailArgs DMArgs :features:tweetdetail :features:dm :features:composer @Multibinds

  153. C D TD ComposerArgs TweetDetailArgs DMArgs :features:tweetdetail :features:dm :features:composer @Multibinds

  154. C D TD ComposerArgs TweetDetailArgs DMArgs :features:tweetdetail :features:dm :features:composer Map<Class<out

    ActivityArgs>, Class<out Activity>> C D TD ComposerActivity DMActivity TweetDetailActivity
  155. @Module object ComposerModule { @Provides @ApplicationScope fun provideActivityClass: Class<out Activity>

    { } } :features:composer @ActivityArgsKey(ComposerArgs::class) @IntoMap return ComposerActivity::class.java
  156. @Module object ComposerModule { @Provides @ApplicationScope fun provideActivityClass: Class<out Activity>

    { } } :features:composer @ActivityArgsKey(ComposerArgs::class) @IntoMap return ComposerActivity::class.java
  157. @Module object ComposerModule { @Provides @ApplicationScope fun provideActivityClass: Class<out Activity>

    { } } :features:composer @ActivityArgsKey(ComposerArgs::class) @IntoMap return ComposerActivity::class.java
  158. @Module object ComposerModule { @Provides @ApplicationScope fun provideActivityClass: Class<out Activity>

    { } } :features:composer @ActivityArgsKey(ComposerArgs::class) @IntoMap return ComposerActivity::class.java
  159. @ApplicationScope class ActivityStarter @Inject constructor( ) private val activityMap: Map<Class<out

    ActivityArgs>, Class<out Activity>> { fun startActivity(context: Context, args: ActivityArgs) { } } val intent = args.toIntent(context, activityMap[args.javaClass]) context.startActivity(intent)
  160. @ApplicationScope class ActivityStarter @Inject constructor( ) { fun startActivity(context: Context,

    args: ActivityArgs) { } } val intent = args.toIntent(context, activityMap[args.javaClass]) context.startActivity(intent) private val activityMap: Map<Class<out ActivityArgs>, Class<out Activity>>
  161. { fun startActivity(context: Context, args: ActivityArgs) { } } @ApplicationScope

    class ActivityStarter @Inject constructor( ) val intent = args.toIntent(context, activityMap[args.javaClass]) context.startActivity(intent) private val activityMap: Map<Class<out ActivityArgs>, Class<out Activity>>
  162. { fun startActivity(context: Context, args: ActivityArgs) { } } @ApplicationScope

    class ActivityStarter @Inject constructor( ) val intent = args.toIntent(context, activityMap[args.javaClass]) context.startActivity(intent) private val activityMap: Map<Class<out ActivityArgs>, Class<out Activity>>
  163. C TD Start an Activity we can’t see at compile

    time
  164. C D TD C D TD Start an Activity we

    can’t see at compile time At compile time you might not see everything, but at runtime it is all there. Let’s use Multibindings to aggregate references at compile time and resolve them at runtime.
  165. None
  166. Initializers

  167. Notification Handlers

  168. Deeplink Handlers

  169. None
  170. None
  171. Subgraphs

  172. Subgraphs are not atomic units

  173. S ApplicationObjectGraph S S S S S S S

  174. S ApplicationObjectGraph M S M S M S M S

    M S M S M S M
  175. S ApplicationObjectGraph M S M S M S M S

    S M S M S M @Component(modules = [ ComposerAppModule::class, TweetDetailAppModule::class, HomeAppModule::class, ExploreAppModule::class, CoreAppModule::class, CoreNetworkAppModule::class, MediaAppModule::class, VideoAppModule::class, NavigationAppModule::class, LoggingAppModule::class, TimelineAppModule::class, … and on and on and on ]) @ApplicationScope interface ApplicationObjectGraph: M
  176. ApplicationObjectGraph M M M M M M M M TimelineAppModule::class,

    … and on and on and on ]) @ApplicationScope interface ApplicationObjectGraph: InitializationSubgraph, ComposerAppSubgraph, TweetDetailAppSubgraph, HomeAppSubgraph, ExploreAppSubgraph, CoreAppSubgraph, CoreNetworkAppSubgraph, MediaAppSubgraph, VideoAppSubgraph, NavigationAppSubgraph, … and on and on and on S S S S S S S S
  177. S M

  178. @Component(modules = [ … and on and on and on

    ]) @ApplicationScope interface ApplicationObjectGraph: InitializationSubgraph, TweetDetailAppSubgraph, @Component(modules = [ … and on and on and on ]) @ApplicationScope interface ApplicationObjectGraph: InitializationSubgraph, MapSubgraph, S M
  179. @Component(modules = [ BroadcastAppModule::class, … and on and on and

    on ]) @ApplicationScope interface ApplicationObjectGraph: InitializationSubgraph, BroadcastAppSubgraph, TweetDetailAppSubgraph, @Component(modules = [ BroadcastAppModule::class, … and on and on and on ]) @ApplicationScope interface ApplicationObjectGraph: InitializationSubgraph, BroadcastAppSubgraph, MapSubgraph, S M
  180. None
  181. { fun broadcastController(): BroadcastController } @Module { @Binds @ApplicationScope fun

    bindBcastController(impl: BroadcastControllerImpl): BroadcastController } object BroadcastAppModule interface BroadcastAppSubgraph
  182. interface BroadcastAppSubgraph { fun broadcastController(): BroadcastController } @Subgraph @Module {

    @Binds @ApplicationScope fun bindBcastController(impl: BroadcastControllerImpl): BroadcastController } object BroadcastAppModule interface BroadcastAppSubgraph
  183. @ApplicationScope interface BroadcastAppSubgraph { fun broadcastController(): BroadcastController } @Subgraph @Module

    { @Binds @ApplicationScope fun bindBcastController(impl: BroadcastControllerImpl): BroadcastController } object BroadcastAppModule interface BroadcastAppSubgraph
  184. @ApplicationScope interface BroadcastAppSubgraph { fun broadcastController(): BroadcastController } @Subgraph @Subgraph(modules

    = [BroadcastAppModule::class]) @Module { @Binds @ApplicationScope fun bindBcastController(impl: BroadcastControllerImpl): BroadcastController } object BroadcastAppModule interface BroadcastAppSubgraph
  185. @ApplicationScope interface ApplicationObjectGraph : BroadcastAppSubgraph, InitializationSubgraph, ... (modules = [BroadcastAppModule::class,

    ...]) @Component
  186. @ApplicationScope interface ApplicationObjectGraph : BroadcastAppSubgraph, InitializationSubgraph, ... (modules = [BroadcastAppModule::class,

    ...]) @Component
  187. @ApplicationScope interface ApplicationObjectGraph : BroadcastAppSubgraph, InitializationSubgraph, ... @ObjectGraph

  188. @UserScope interface UserObjectGraph : UserManagerSubgraph, TweetRepository, ... (modules = [UserManagerModule::class,

    ...]) @ApplicationScope interface ApplicationObjectGraph : BroadcastAppSubgraph, InitializationSubgraph, ... @ObjectGraph @Subcomponent
  189. @UserScope interface UserObjectGraph : UserManagerSubgraph, TweetRepository, ... (modules = [UserManagerModule::class,

    ...]) @ApplicationScope interface ApplicationObjectGraph : BroadcastAppSubgraph, InitializationSubgraph, ... @ObjectGraph @Subcomponent
  190. @UserScope interface UserObjectGraph : UserManagerSubgraph, TweetRepository, ... @ApplicationScope interface ApplicationObjectGraph

    : BroadcastAppSubgraph, InitializationSubgraph, ... @ObjectGraph @ObjectGraph
  191. @ObjectGraph @Subgraph

  192. Annotation Processor @ObjectGraph @Subgraph @Component @Subcomponent

  193. @ObjectGraph @Subgraph Annotation Processor @Component @Subcomponent

  194. Annotation Processor @ObjectGraph @Subgraph @Component @Subcomponent

  195. Fully Dagger Compatible Annotation Processor

  196. Subgraphs

  197. Subgraphs are not encapsulated

  198. S S S S S S

  199. S S S S S S

  200. S S S S S S

  201. S

  202. S @ApplicationScope interface BroadcastAppSubgraph { fun broadcastController(): BroadcastController } @Subgraph(modules

    = [BroadcastAppModule::class])
  203. @Subgraph( subgraphDependencies = [ CoreNetworkSubgraph::class, VideoPlayerSubgraph::class ]) S @ApplicationScope interface

    BroadcastAppSubgraph { fun broadcastController(): BroadcastController } @Subgraph @Subgraph(modules = [BroadcastAppModule::class],
  204. S ApplicationObjectGraph S S S S S S S

  205. S ApplicationObjectGraph S S S S S S S

  206. S ApplicationObjectGraph S S S S S S S

  207. Subgraphs

  208. Subgraphs are not self documenting

  209. Subgraphs are not self documenting

  210. @Subgraph( subgraphDependencies = [ CoreNetworkSubgraph::class, VideoPlayerSubgraph::class ]) @ApplicationScope interface BroadcastAppSubgraph

    { fun broadcastController(): BroadcastController } @Subgraph @Subgraph(modules = [BroadcastAppModule::class],
  211. @Subgraph( subgraphDependencies = [ CoreNetworkSubgraph::class, VideoPlayerSubgraph::class ]) @ApplicationScope interface BroadcastAppSubgraph

    { fun broadcastController(): BroadcastController } @Subgraph @Subgraph(modules = [BroadcastAppModule::class],
  212. @Subgraph( subgraphDependencies = [ CoreNetworkSubgraph::class, VideoPlayerSubgraph::class ]) @ApplicationScope interface BroadcastAppSubgraph

    { fun broadcastController(): BroadcastController } @Subgraph @Subgraph(modules = [BroadcastAppModule::class],
  213. @Subgraph( subgraphDependencies = [ CoreNetworkSubgraph::class, VideoPlayerSubgraph::class ]) @ApplicationScope interface BroadcastAppSubgraph

    { fun broadcastController(): BroadcastController } @Subgraph @Subgraph(modules = [BroadcastAppModule::class],
  214. Graphs

  215. Graphs are hard to maintain

  216. @ObjectGraph @ApplicationScope interface ApplicationObjectGraph: InitializationSubgraph, BroadcastAppSubgraph, TweetDetailAppSubgraph, HomeAppSubgraph, ExploreAppSubgraph, CoreAppSubgraph,

    CoreNetworkAppSubgraph, MediaAppSubgraph, VideoPlayerSubgraph, NavigationAppSubgraph, TweetUploadAppSubgraph, PerformanceMetricsAppSubgraph, DMAppSubgraph, JSONModelsAppSubgraph, DynamicModulesAppSubgraph, NotificationsAppSubgraph,
  217. @ObjectGraph @ApplicationScope interface ApplicationObjectGraph: InitializationSubgraph, BroadcastAppSubgraph, TweetDetailAppSubgraph, HomeAppSubgraph, ExploreAppSubgraph, CoreAppSubgraph,

    CoreNetworkAppSubgraph, MediaAppSubgraph, VideoPlayerSubgraph, NavigationAppSubgraph, TweetUploadAppSubgraph, PerformanceMetricsAppSubgraph, DMAppSubgraph, JSONModelsAppSubgraph, DynamicModulesAppSubgraph, NotificationsAppSubgraph, This is actually insane
  218. Annotation Processor @ObjectGraph @ApplicationScope interface ApplicationObjectGraph: InitializationSubgraph, BroadcastAppSubgraph, TweetDetailAppSubgraph, HomeAppSubgraph,

    ExploreAppSubgraph, CoreAppSubgraph, CoreNetworkAppSubgraph, MediaAppSubgraph, VideoPlayerSubgraph, NavigationAppSubgraph, TweetUploadAppSubgraph, PerformanceMetricsAppSubgraph, DMAppSubgraph, JSONModelsAppSubgraph, DynamicModulesAppSubgraph, NotificationsAppSubgraph,
  219. Annotation Processor @ObjectGraph @ApplicationScope interface ApplicationObjectGraph: InitializationSubgraph, BroadcastAppSubgraph, TweetDetailAppSubgraph, HomeAppSubgraph,

    ExploreAppSubgraph, CoreAppSubgraph, CoreNetworkAppSubgraph, MediaAppSubgraph, VideoPlayerSubgraph, NavigationAppSubgraph, TweetUploadAppSubgraph, PerformanceMetricsAppSubgraph, DMAppSubgraph, JSONModelsAppSubgraph, DynamicModulesAppSubgraph, NotificationsAppSubgraph,
  220. @ObjectGraph @ApplicationScope interface ApplicationObjectGraph Annotation Processor Automatic Discovery

  221. S ApplicationObjectGraph S S S S S S S

  222. S ApplicationObjectGraph S S S S S S S

  223. Subgraphs

  224. Subgraphs are not reusable

  225. CoreNetworkSubgraph OkHttpClient Cache Set<Interceptor> CoreNetworkSubgraph :core:network

  226. PeriscopeNetworkSubgraph :periscope:network CoreNetworkSubgraph OkHttpClient Cache Set<Interceptor> CoreNetworkSubgraph :core:network

  227. CoreNetworkSubgraph OkHttpClient Cache Set<Interceptor> CoreNetworkSubgraph :core:network PeriscopeNetworkSubgraph :periscope:network

  228. open class CoreNetworkSubgraph { fun okHttpClient(): OkHttpClient = … fun

    interceptors(): Set<Interceptor> = … } fun cache(): Cache = Cache()
  229. class PeriscopeNetworkSubgraph : CoreNetworkSubgraph { override fun cache(): Cache =

    PeriscopeCachePolicy() } open fun cache(): Cache = Cache() open class CoreNetworkSubgraph { fun okHttpClient(): OkHttpClient = … fun interceptors(): Set<Interceptor> = … }
  230. @Subgraph(module = [CoreNetworkModule::class]) @Open interface CoreNetworkSubgraph { fun okHttpClient(): OkHttpClient

    @Open fun cache(): Cache fun interceptors(): Set<Interceptor> }
  231. @Subgraph(module = [CoreNetworkModule::class]) @Open interface CoreNetworkSubgraph { fun okHttpClient(): OkHttpClient

    @Open fun cache(): Cache fun interceptors(): Set<Interceptor> } @Subgraph interface PeriscopeNetworkSubgraph : CoreNetworkSubgraph { @Override @Binds fun bindCache(cache: PeriscopeCache): Cache }
  232. Subgraphs

  233. Subgraphs don’t support inversion of dependencies

  234. interface DatabaseSubgraph { fun migrationManager(): MigrationManager } fun schema(): Schema

    @Subgraph
  235. interface DatabaseSubgraph { fun migrationManager(): MigrationManager } fun schema(): Schema

    @Subgraph @Abstract @Abstract
  236. interface DatabaseSubgraph { fun migrationManager(): MigrationManager } fun schema(): Schema

    @Subgraph @Abstract @Abstract @Subgraph interface TwitterDatabaseSubgraph : DatabaseSubgraph { @Binds fun schema(bound: TwitterSchema): Schema }
  237. And more!

  238. And more!

  239. And more!

  240. And more!

  241. Annotation Processor

  242. Scythe Scythe

  243. Scythe OOP concepts for DI IDE support Better build times

    Binding overrides Modular scalability Self documentation Reusability Automatic discovery Completeness validation Factory bindings Assisted Injection Compatible with Dagger
  244. Scythe Open-source launch for everybody in 2020

  245. @CesarDielo César @mrmans0n Nacho

  246. @CesarDielo César @mrmans0n Nacho Thank you! More questions? Follow ups

    from this talk? Hit us up on Twitter!