Summary
The current docs around @KoinApplication, @Configuration, and cross-module @ComponentScan are easy to read as if they all solve the same problem.
After building a minimal KMP repro, I think the docs should explicitly distinguish:
- discovering leaf annotated classes across Gradle modules
- auto-loading modules into an app via
@KoinApplication
- starter/library author patterns where the app should not need explicit
modules = [...]
Docs page:
https://insert-koin.io/docs/reference/koin-annotations/modules/
Repro repo:
https://github.com/zjarlin/koin-scan-test
What the repro verifies
The repro is a commonMain KMP multi-module project with one app module and several feature modules.
Verified cases:
- A
commonMain root with @ComponentScan("site.addzero.repro") can discover cross-module leaf @Single chains.
- A plain provider
@Module in a dependency is not auto-loaded just because the app uses wide scan.
- A library can self-register only when it exposes a public
@Configuration starter module, or when the app explicitly lists the module, or when another loaded module includes it.
- For a single starter entry module that relies on same-package external
@Single classes, @ComponentScan is still required.
The last point is important for starter-style modules shaped like:
@Module
@ComponentScan
@Configuration
class BannerModule
This shape works for self-registration.
A control case with:
@Module
@Configuration
class BannerModule
still makes the module discoverable, but does not pick up the same-package external @Single classes.
Why this feels ambiguous in the current docs
The docs currently show cross-module discovery and @Configuration auto-discovery, but they do not clearly separate:
- app author usage
- library/starter author usage
- single-entry starter modules vs plain provider modules
That makes it easy to assume any dependency module with @Module will be picked up automatically once the app uses @KoinApplication and/or wide @ComponentScan, which is not what the repro shows.
Requested doc additions
Could you please add explicit examples for these cases?
- App-owned module
- The app explicitly references modules via
modules = [...]
- Starter/library self-registration
- A public
@Configuration starter module wrapping internal providers
- Single-entry starter module
@Module + @ComponentScan + @Configuration
- and a note explaining why
@ComponentScan is still needed when definitions live outside the module body
- What can be omitted
- when
configurations = [...] can be omitted
- when
modules = [...] can be omitted
- when
@ComponentScan can be omitted
Example patterns that would help
Wrapped provider starter:
@Module
class InternalProviderModule {
@Single
fun provideX(): X = ...
}
@Module(includes = [InternalProviderModule::class])
@Configuration
class MyStarterModule
Single-entry starter:
@Single
class MyService(...)
@Module
@ComponentScan
@Configuration
class MyStarterModule
If I misunderstood the intended behavior, I would also be happy to know which part of the repro is relying on unsupported behavior. But from the generated graph and tests, this looks like a documentation gap rather than a runtime bug.
Summary
The current docs around
@KoinApplication,@Configuration, and cross-module@ComponentScanare easy to read as if they all solve the same problem.After building a minimal KMP repro, I think the docs should explicitly distinguish:
@KoinApplicationmodules = [...]Docs page:
https://insert-koin.io/docs/reference/koin-annotations/modules/
Repro repo:
https://github.com/zjarlin/koin-scan-test
What the repro verifies
The repro is a
commonMainKMP multi-module project with one app module and several feature modules.Verified cases:
commonMainroot with@ComponentScan("site.addzero.repro")can discover cross-module leaf@Singlechains.@Modulein a dependency is not auto-loaded just because the app uses wide scan.@Configurationstarter module, or when the app explicitly lists the module, or when another loaded module includes it.@Singleclasses,@ComponentScanis still required.The last point is important for starter-style modules shaped like:
This shape works for self-registration.
A control case with:
still makes the module discoverable, but does not pick up the same-package external
@Singleclasses.Why this feels ambiguous in the current docs
The docs currently show cross-module discovery and
@Configurationauto-discovery, but they do not clearly separate:That makes it easy to assume any dependency module with
@Modulewill be picked up automatically once the app uses@KoinApplicationand/or wide@ComponentScan, which is not what the repro shows.Requested doc additions
Could you please add explicit examples for these cases?
modules = [...]@Configurationstarter module wrapping internal providers@Module + @ComponentScan + @Configuration@ComponentScanis still needed when definitions live outside the module bodyconfigurations = [...]can be omittedmodules = [...]can be omitted@ComponentScancan be omittedExample patterns that would help
Wrapped provider starter:
Single-entry starter:
If I misunderstood the intended behavior, I would also be happy to know which part of the repro is relying on unsupported behavior. But from the generated graph and tests, this looks like a documentation gap rather than a runtime bug.