Application, Configuration and Modules
Application Bootstrap with @KoinApplication
Use @KoinApplication to define your application entry point:
@KoinApplication(modules = [MyModule::class])
class MyApp
Start Koin using the typed API:
fun main() {
startKoin<MyApp>()
// Or with configuration
startKoin<MyApp> {
printLogger()
}
}
Available Typed APIs
| API | Description |
|---|---|
startKoin<T>() | Start Koin globally |
startKoin<T> { } | Start with configuration block |
koinApplication<T>() | Create isolated KoinApplication |
koinConfiguration<T>() | Create configuration (for Compose, Ktor) |
module<T>() | Load a single @Module class |
modules(A::class, B::class) | Load multiple @Module classes |
Loading Individual Modules
Use module<T>() or modules(vararg KClass) to load @Module classes directly, without needing @KoinApplication:
startKoin {
module<NetworkModule>()
modules(DataModule::class, CacheModule::class)
}
This is useful for tests or when mixing annotation modules with DSL configuration:
// In tests — load only the modules you need
@get:Rule
val koinTestRule = KoinTestRule.create {
module<NetworkModule>()
}
module<T>() and modules(vararg KClass) are stub functions that the compiler plugin intercepts and transforms at compile time. They require the Koin Compiler Plugin to be applied.
@KoinApplication Parameters
modules: Array of module classes to includeconfigurations: Array of configuration labels to load
@KoinApplication(
modules = [CoreModule::class],
configurations = ["production"]
)
class ProdApp
When no configurations are specified, modules marked with @Configuration (default label) are loaded automatically.
Module Load Order and Overrides
Koin is last-wins at runtime: when two modules define the same type, the one loaded last takes precedence. The compiler plugin assembles the module list from @KoinApplication in this order:
- Auto-discovered
@Configurationmodules (local + dependency JARs) — load first - Explicit
@KoinApplication(modules = [A, B, C])— load last, in declaration order
So app-level overrides always win over dependency defaults:
// In a dependency library module
@Module @Configuration
class CoreModule {
@Singleton fun feature(): Feature = DefaultFeature()
}
// In your app module
@Module
class AppModule {
@Singleton fun feature(): Feature = AppFeature() // custom override
}
@KoinApplication(modules = [AppModule::class])
class MyApp
// Load order: CoreModule (DefaultFeature) → AppModule (AppFeature wins)
// Runtime get<Feature>() returns AppFeature.
Within the explicit list, the declared order is preserved — so @KoinApplication(modules = [A, B, C]) loads A, then B, then C, and C wins among those three. Each entry's @Module(includes = [...]) chain stays grouped with that entry.
If a module appears in both the explicit list and is also @Configuration-discovered, it loads once — at its explicit position — so the declaration order in modules = [...] always controls override precedence.
If you need a specific load order between several @Configuration modules (instead of the classpath-scan order), list them explicitly in @KoinApplication(modules = [Core::class, Feature::class, App::class]) — the explicit list honours declaration order.
Configuration Management with @Configuration
The @Configuration annotation allows you to organize modules into different configurations (environments, flavors, etc.). This is useful for organizing modules by deployment environment or feature sets.
Basic Configuration Usage
// Put module in default Configuration
@Module
@Configuration
class CoreModule
The default configuration is named "default", can be used with @Configuration or @Configuration("default")
You need to use the @KoinApplication to be able to scan modules from configuration:
// module A
@Module
@Configuration
class ModuleA
// module B
@Module
@Configuration
class ModuleB
// module App, scan all @Configuration modules
@KoinApplication
object MyApp
Multiple Configuration Support
A module can be associated with multiple configurations:
// This module is available in both "prod" and "test" configurations
@Module
@Configuration("prod", "test")
class DatabaseModule {
@Single
fun database() = PostgreSQLDatabase()
}
// This module is available in default, test, and development
@Module
@Configuration("default", "test", "development")
class LoggingModule {
@Single
fun logger() = Logger()
}
Environment-Specific Configurations
// Development-only configuration
@Module
@Configuration("development")
class DevDatabaseModule {
@Single
fun database() = InMemoryDatabase()
}
// Production-only configuration
@Module
@Configuration("production")
class ProdDatabaseModule {
@Single
fun database() = PostgreSQLDatabase()
}
// Available in multiple environments
@Module
@Configuration("default", "production", "development")
class CoreModule {
@Single
fun logger() = Logger()
}
Using Configurations with @KoinApplication
By default, the @KoinApplication is loading all default configurations (modules tagged with @Configuration)
You can also reference these configurations in your application bootstrap:
@KoinApplication(configurations = ["default", "production"])
class ProductionApp
@KoinApplication(configurations = ["default", "development"])
class DevelopmentApp
// Load only default configuration (same as @KoinApplication with no parameters)
@KoinApplication
class SimpleApp
- Empty
@Configurationis equivalent to@Configuration("default") - The "default" configuration is loaded automatically when no specific configurations are specified
- Modules can belong to multiple configurations by listing them in the annotation
Organizing with Modules
Always organize your definitions in explicit modules using @Module:
Class Module with @Module
To declare a module, tag a class with @Module annotation:
@Module
class MyModule
Reference modules in your @KoinApplication:
@KoinApplication(modules = [MyModule::class])
class MyApp
fun main() {
startKoin<MyApp>()
}
Components Scan with @ComponentScan
Use @ComponentScan to automatically discover annotated components:
@Module
@ComponentScan
class MyModule
This scans the current package and subpackages for annotated components. Specify a package explicitly:
@Module
@ComponentScan("com.myapp.features")
class FeatureModule
@ComponentScan traverses across all Gradle modules for the same package.
Definitions in Class Modules
To define a definition directly in your code, you can annotate a function with definition annotations:
// given
// class MyComponent(val myDependency : MyDependency)
@Module
class MyModule {
@Single
fun myComponent(myDependency : MyDependency) = MyComponent(myDependency)
}
Note:
@InjectedParam(for injected parameters from startKoin) and@Property(for property injection) are also usable on function members. See the definitions documentation for more details on these annotations.
Including Modules
Use the includes attribute to compose modules:
@Module
class ModuleA
@Module(includes = [ModuleA::class])
class ModuleB
Reference the root module in your application:
@KoinApplication(modules = [ModuleB::class]) // Includes ModuleA automatically
class MyApp
fun main() {
startKoin<MyApp>()
}