Feature/analyse dependencies graph#36
Conversation
… coming up with something else similar
| implementation libraries.kotlinCoroutinesCore | ||
| implementation libraries.kotlinStdLib | ||
|
|
||
| kapt libraries.daggerCompiler |
There was a problem hiding this comment.
It's always a good practice to add the dagger compiler to Gradle modules that define Dagger modules or have classes annotated with @Inject constructor()
it wouldn't happen in the current project setup, but in some cases when sharing a Gradle module you might get the Factory of that class only created by the module that uses it as a dependency and you could get multiple instances of the same Factory generated, causing a compilation issue :)
There was a problem hiding this comment.
See Zac Sweer's blog post for more information on this:
https://www.zacsweers.dev/dagger-party-tricks-refactoring/
The trick here is simple: any project you have constructor injection, you should run the dagger compiler over it too.
| apply plugin: 'kotlin-kapt' | ||
|
|
||
| dependencies { | ||
| implementation project(':actor_detail_data') |
There was a problem hiding this comment.
dependency was inverted
"_data modules know about the "data stores", not the other way around
There was a problem hiding this comment.
Interesting that remote depended on data before this change. Usually we try to avoid the feature knowing about where data is coming from (i.e. remote or cache), so this change makes sense to me.
Is it possible for remote to be implementation instead of api so that the implementation detail doesn't get leaked to the feature?
There was a problem hiding this comment.
That's a good shout.
I think I tried that before and it didn't work initially but I could give it another go with a different approach this time
| @@ -1,4 +1,4 @@ | |||
| package com.muvi.actor_detail_data | |||
| package com.muvi.actor_detail_remote | |||
There was a problem hiding this comment.
moved the class to invert the dependency
| AppModule::class, | ||
| LetterboxdApiModule::class | ||
| ] | ||
| modules = [ |
There was a problem hiding this comment.
sorry about this. Maybe the coding style could be added to the project at some point? 😬
| } | ||
| } | ||
|
|
||
| private fun buildConfig(): ApiConfig { |
There was a problem hiding this comment.
This was the way I found to keep :letterboxd_api as a kotlin-only module
| apply plugin: 'kotlin-kapt' | ||
|
|
||
| dependencies { | ||
| implementation project(':base_data') |
There was a problem hiding this comment.
couldn't really see the benefit of :base_data so I deleted it 😅
| @@ -1,8 +1,10 @@ | |||
| apply plugin: 'kotlin' | |||
| apply plugin: 'com.android.library' | |||
There was a problem hiding this comment.
had to be transformed into an android library module so it can depend on :feed_cache
| import dagger.Provides | ||
|
|
||
| @Module | ||
| @Module(includes = [ |
There was a problem hiding this comment.
this helps to simplify the dagger graph as well
A nice way of thinking about this is that |
| implementation project(':letterboxd_api') | ||
| api project(':letterboxd_api') | ||
| api project(':navigation') | ||
| api project(':design_library') |
There was a problem hiding this comment.
I'm not sold on core depending on design_library. You may build a feature that has completely bespoke UI elements that doesn't need anything from design_library.
This would be one of the first things I'd cut from core if I needed to save space for a 4MB instant app
|
@condesales can you share the script or process to generate the DI graph? |
|
So after looking at the script. I believe I need to update the module names in the |
|
Just curious, When will this be getting merged? @jamieadkins95 |
Yeah, unfortunately, to get a good plot we had to hardcode some of the prefixes/suffixes of the names of the modules. |
This is a proposal on how to handle the dependency between modules.
I used Jake Wharton's Gradle script with some minor changes to plot the dependency graph to help visualize it.
Original Graph


Proposed Graph
The changes mainly follow the idea of using dependencies as
apiinstead ofimplementationwherever I thought it made sense.The
oniondiagram from Clean Arch always has thedatalayer as a superset ofdomain. By using that idea we would expect that when addingdataas a dependency you would have access to itsdomainI applied the same logic to the cache/remote "data stores" (not sure how you call them)
From the original graph, we could also see that
:design_libraryand:navigationmodules are all accessed by the end-feature modules:feed,:actor_detailsand:film_details.I believe the whole idea of a
:coremodule is to share all the shared dependencies between modules so moved those asapidependencies there.One interesting case is for the
:letterboxd_api. That module theoretically should only be accessed byremotemodules but we need it as a dependency to:corebecause we need to build the dagger graph and provide it as@Singleton. Perhaps by using@Reusablewe could achieve the same outcome?It's important to be intelligent about what goes into
:coreas dependencies. Only those that are shared across all features should go there as we don't want to provide dependencies that are not needed by features. This could be a big impact on dynamic features.I'm going to leave some comments on specific lines to explain some specific decisions, but the above summarises the whole idea.
I'd love to hear from you the thought process behind the original graph dependencies setup. I dived into this specifically because I was curious to see how other people have been tackling the same problem, so thanks for sharing!