Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Scaling Dagger: DI in a modular world

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.

Nacho L.

December 20, 2019
Tweet

More Decks by Nacho L.

Other Decks in Programming

Transcript

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

    López Software Engineer Android Foundation Architecture
  2. Periscope @PeriscopeCo You might have heard some news: It involves

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

  4. D D

  5. 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.
  6. 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)
  7. class Renderer (database: Database, renderer: Renderer) class Timeline class Database

    () (inflater: LayoutInflater) @Inject constructor @Inject constructor @Inject constructor
  8. { fun featureRetainedBuilder(): FeatureRetainedObjectGraph.Builder } @Subcomponent(modules = [UserModule::class]) @UserScope interface

    UserObjectGraph @Component(modules = [ApplicationModule::class]) @ApplicationScope interface ApplicationObjectGraph { fun userObjectGraphBuilder(): UserObjectGraph.Builder }
  9. { 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
  10. { 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 }
  11. { 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
  12. Components for the root graph for the nested graphs Subcomponents

    @ApplicationScope @UserScope @RetainedScope @ViewScope
  13. @Component @ApplicationScope interface ApplicationObjectGraph } fun composerLauncher(): ComposerLauncher fun composerNotificationHandler():

    ComposerNotifHandler fun tweetsRepository(): TweetsRepository fun tweetsDataSource(): TweetsDataSource fun imageLoader(): ImageLoader { , TweetRepositoryAppModule::cl (modules = [ComposerAppModule::class
  14. 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
  15. 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
  16. 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
  17. @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
  18. : @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
  19. : @Component @ApplicationScope interface ApplicationObjectGraph { } fun okHttpClient(): OkHttpClient

    fun twitterApiService(): TwitterApiService fun tweetsRepository(): TweetsRepository fun tweetsDataSource(): TweetsDataSource fun imageLoader(): ImageLoader ComposerAppSubgraph :app:twitter
  20. @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
  21. @Component @ApplicationScope interface ApplicationObjectGraph , : { } fun okHttpClient():

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

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

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

    twitterApiService(): TwitterApiService ComposerAppSubgraph TweetRepositoryAppSubgraph MediaAppSubgraph , interface CoreNetworkAppSubgraph { } CoreNetworkAppSubgraph , :core:network :app:twitter
  25. 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.
  26. component = .application(this) .build() class MyApplication : Application { private

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

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

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

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

    Dagger { companion object { inline fun <reified B : Any> getObjectGraphBuilder(): B = getObjectGraphBuilder(B::class.java) } {
  31. 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) } {
  32. 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 {
  33. 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! {
  34. Referencing generated code Use reflection once to grab the root

    object graph builder. Dagger.getObjectGraphBuilder<ApplicationObjectGraph>() DaggerApplicationObjectGraph.builder()
  35. component = Dagger.getObjectGraphBuilder<ApplicationObjectGraph>() .application(this) .build() class MyApplication : Application {

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

    private lateinit var component: ApplicationObjectGraph override fun onCreate() { } super.onCreate() }
  37. object ApplicationObjectGraphProvider { } fun <T> getSubgraph(clazz: Class<T>): T var

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

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

    } companion object { fun get(): MediaAppSubgraph { return ApplicationObjectGraphProvider.graphProvider .getSubgraph(MediaAppSubgraph::class.java) }
  40. 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
  41. 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) }
  42. 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) }
  43. 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)
  44. 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)
  45. 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
  46. @Module object ComposerModule { @Provides @ApplicationScope fun provideActivityClass: Class<out Activity>

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

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

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

    { } } :features:composer @ActivityArgsKey(ComposerArgs::class) @IntoMap return ComposerActivity::class.java
  50. @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)
  51. @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>>
  52. { 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>>
  53. { 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>>
  54. 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.
  55. 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
  56. 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
  57. S M

  58. @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
  59. @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
  60. { fun broadcastController(): BroadcastController } @Module { @Binds @ApplicationScope fun

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

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

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

    = [BroadcastAppModule::class]) @Module { @Binds @ApplicationScope fun bindBcastController(impl: BroadcastControllerImpl): BroadcastController } object BroadcastAppModule interface BroadcastAppSubgraph
  64. @UserScope interface UserObjectGraph : UserManagerSubgraph, TweetRepository, ... (modules = [UserManagerModule::class,

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

    ...]) @ApplicationScope interface ApplicationObjectGraph : BroadcastAppSubgraph, InitializationSubgraph, ... @ObjectGraph @Subcomponent
  66. S

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

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

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

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

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

    { fun broadcastController(): BroadcastController } @Subgraph @Subgraph(modules = [BroadcastAppModule::class],
  72. @ObjectGraph @ApplicationScope interface ApplicationObjectGraph: InitializationSubgraph, BroadcastAppSubgraph, TweetDetailAppSubgraph, HomeAppSubgraph, ExploreAppSubgraph, CoreAppSubgraph,

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

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

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

    ExploreAppSubgraph, CoreAppSubgraph, CoreNetworkAppSubgraph, MediaAppSubgraph, VideoPlayerSubgraph, NavigationAppSubgraph, TweetUploadAppSubgraph, PerformanceMetricsAppSubgraph, DMAppSubgraph, JSONModelsAppSubgraph, DynamicModulesAppSubgraph, NotificationsAppSubgraph,
  76. open class CoreNetworkSubgraph { fun okHttpClient(): OkHttpClient = … fun

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

    PeriscopeCachePolicy() } open fun cache(): Cache = Cache() open class CoreNetworkSubgraph { fun okHttpClient(): OkHttpClient = … fun interceptors(): Set<Interceptor> = … }
  78. @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 }
  79. interface DatabaseSubgraph { fun migrationManager(): MigrationManager } fun schema(): Schema

    @Subgraph @Abstract @Abstract @Subgraph interface TwitterDatabaseSubgraph : DatabaseSubgraph { @Binds fun schema(bound: TwitterSchema): Schema }
  80. 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