Skip to content

Docs: clarify @Module vs @Configuration vs @ComponentScan for cross-module starter discovery #341

Description

@zjarlin

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:

  1. discovering leaf annotated classes across Gradle modules
  2. auto-loading modules into an app via @KoinApplication
  3. 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?

  1. App-owned module
    • The app explicitly references modules via modules = [...]
  2. Starter/library self-registration
    • A public @Configuration starter module wrapping internal providers
  3. Single-entry starter module
    • @Module + @ComponentScan + @Configuration
    • and a note explaining why @ComponentScan is still needed when definitions live outside the module body
  4. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions