I know that dagger is a complete compile-time libr...
# dagger
h
I know that dagger is a complete compile-time library. right? I've used hilt and koin, but never pure dagger. Does this problem occur in pure dagger as well?
g
It's the same in dagger So it's really about how you structure your code, if app compiles component which uses this dependency, it must know it's type on compile time
❤️ 1
There is a difference comparing to hilt, that with dagger you are not forced to use subcomponents, unlike hilt, so each module can have own component with own graph, do app doesn't have to know how to compile components in other modules But subcomponents are compiled on the side of app always, one if big disadvantages of this approach
h
@gildor Thanks, now I see why people use Hilt ...haha I was aiming for clear separation of concerns with a multi-module setup, but this is starting to worry me a bit.
The problem isn't just this one, but it also happens like this when i inject a dispatcher like nia(now-in-android). In the end, is it best to register as compileonly?
I heard that it can be extracted separately like the "core-di" module, but I don't think it is scalable.
p
Are you adding the dependency using
api
or
implementation
in your network module? api will expose your transitive dependencies to your module consumers while implementation won't
In general is safer implementation although api is convenient
h
@Pablichjenkov Are you asking how I registered the ktor library as a dependency in the network module? I want to register it as impl, but it is like this now due to the characteristics(?) of the kotlin-inject library.
I think We have to choose one of the two methods: registering ktor as an API in the network module or registering ktor as compileOnly in the app module. (I don't want this method since I only used hilt and koin, but it seems like there is no other way, right?)
Actually, I was building a scalable architecture, so I had a base-network module, and service-xxx:network-impl, service-xxx:network-public(api), and so on. So I want to avoid this method even more. (service-xxx:data-impl, service-xxx:data-public, service-xxx:domain-impl, service-xxx:domain-public, etc. Examples to help you understand)
👍 1
w
In the end, api vs implementation boils down to whether you're exposing a class from the dependency as part the module's public api or not. If you are exposing a class but use implementation depenendency, then consuming modules will not have access to classes from the public api. To me, this sounds wrong. The other part is whether you're using subcomponents or not, as that affects what goes into the public api. Personally I'd say that the only module that depends on
impl
modules should be the module with DI (so
app
). If that's the case, then it doesn't really matter whether you're using api or implementation+compileOnly, app will see the classes anyway. Imo
api
dependencies should be used if the classes from that dependency are in public api and that's simplest way to go about that. A nice benefit is that if the dependencies of the impl module change, you only need to adjust that in one place
3
p
Right, The reason I like implementation even though I have to duplicate it in all modules, it is more explicit on the dependencies you depend on, easier for custom pom.xml generation. Also it allows to use a different version of the Library although this could be seen as a disadvantage
h
Thank you all. Actually, I don't know much about subcomponents, so I think I need to study about it a bit.
t
Personally I quite like subcomponents. Far easier to maintain than component dependencies over time, both have their usecase though. As far as api/impl, most people get it wrong or have their project slowly drift to wrong. Best way to get that right is with a plugin to help validate your configuration so your build remains configured correctly. https://github.com/autonomousapps/dependency-analysis-gradle-plugin
👍 1
g
> Personally I quite like subcomponents. Far easier to maintain than component dependencies over time, both have their usecase though. They are for sure easier to write and maintain, because you can request any dependency from any class, but their flexible nature promotes bad practices: you really don't know to which classes your module depends, so it's harder to review that inversion of control on module-level is preserved and avoid creating a too tightly coupled dependencies without proper encapsulations of higher-level components. So in perfecet case you want to depend on other components, not on their implementation detilas And they scale way worse. They're pretty terrible with Dagger, where they could be a scalability bottleneck. All subcomponent code is put in a single app-level Java class with component, which is very slow to compile, and it has to be recompiled every time anything changes. Better with Metro, but with dagger subcomponents, really a time bomb, or if it will never blow up, it makes the whole compilation slower and slower with every new subcomponent and doesn't allow to move graph calculation and compilation to modules (which also will be parallel), all have to be done on app level sequentially So it's not only about implicit vs explicit dependency, but also about encapsulating compilation on module level
❤️ 1
Also, components could have truly internal dependencies, so do not expose them to the app, which is related to the original question. For subcomponent, every single dependency of which have to be known by app, because subcomponent is just a part of app component
@HyeonBae Ji
Thanks, now I see why people use Hilt ...haha
Not sure that I get it. And why? Hilt is convenient and easy to use, and it's officially promoted solution to inject Android components, but I doubt that many really understand how it works under the hood
t
Yeah hilt is just generating components and subcomponents via more build complexity so dagger usage becomes extremely guided and mostly trivial. Appeases the crowd that couldn't comprehend the thermosiphon example (which honestly was brilliant, a coffee joke for a java tool while combining two abstract physical concepts into a single physical implementation). Fits well with the Google/Android incentive model too. They make money via ads/play store tax on purchases. Make better apps easier to write -> potential for more better apps -> more potential for revenue.