Hello, to this day I'm using a API/IMPL modules sy...
# dagger
g
Hello, to this day I'm using a API/IMPL modules system for all our "services" to share them across our Android Project: • Module_API has an interface, all modules that need that service depend on this • Module_IMPL has the implementation to that interface + dagger module to bind those two together, and is used only by the our android application module Now, I just realized we can actually use
internal
in one module instead of two and hide all the implementation + dagger module by declaring the dagger module like this:
Copy code
interface SomeInterface { .. }
internal class DefaultImpl(...): SomeInterface { ... }  

@InstallIn(SingletonComponent::class)
@Module
internal abstract class SomeInterfaceDaggerModule {

    @Binds
    abstract fun bindsSomeInterfaceHere(impl: DefaultImpl): SomeInterface
}
And to my surprise, this works! But does it work because the generated code is Java and the concept of
internal
doesn't exist in Java, or am I missing something else here? If so, the day dagger will generate code in Kotlin through KSP this trick will stop working, right? 🤔
a
This could be the result of Hilt's classpath aggregation, which makes it "see" transitive dependencies even when they're effectively hidden at module boundaries (with
implementation
).
InternalKtModuleTest.testBindingFromInternalKtModule looks like a test verifying this behavior — it accesses values from
internal
Dagger modules that are defined in a separate compilation module
g
Thanks @alex.krupa, super interesting! So that means that switching to generating Kotlin code (dagger with KSP) should not break that compatibility, right?
a
Yeah, I think we can trust the Dagger team here
g
Thanks for your reinforcing opinion. Sounds like we'll manage to reduce the # of our modules by a lot :))
a
Are you splitting api/impl for modular indirection? Having DI wirings in separate compilation modules still has advantage of being able to switch implementations at compile time, e.g. to provide fake implementations in sample apps.
g
I have indeed split API and IMPL to avoid circular dependency and was thinking of actually putting them back together, but this blogpost goes even further and splits IMPL into DI and pure IMPL. Interesting!
@JordNullable Well written blogposts! I wonder what you think of actually using
internal
instead of splitting, any thoughts?
j
Hello! I didn’t know that Dagger would work so nicely with the
internal
modifier, if ksp maintains that behaviour then I think this would work very well.
I like to advocate for the additional
:di
module because I think it is nice to keep your dependency injection separate from implementations. Like Alex says it is easier to swap implementations for fakes. There is always that chance (a very small one) you might change your DI library and having it separate is nice. It does mean the creation of a lot of extra Gradle projects, which is costly in larger projects but the configuration cache makes that less of a big deal
Having your DI classes on your development classpath is something I like to avoid. Using internal would be a big win in your two project set up!