Skip to main content
Version: 4.2

Modules

A module groups related definitions, by feature or by layer. This page covers how to declare modules, compose them, scan packages for annotated classes, discover modules across Gradle modules, and in which order modules load. For the definitions inside a module, see Definitions.

Declare a module​

import org.koin.dsl.module
import org.koin.plugin.module.dsl.*

val networkModule = module {
single<ApiClient>()
single<TokenManager>()
}

val dataModule = module {
single<Database>()
single<UserRepository>()
}

A definition can depend on a definition from another module. Koin resolves across every loaded module, so UserRepository in dataModule can use ApiClient from networkModule without any import between modules. Both modules must be loaded, see Starting Koin.

Module composition with includes()​

A module can include other modules. Loading the parent loads the whole tree:

val networkModule = module {
single<ApiClient>()
}

val storageModule = module {
single<Database>()
}

val dataModule = module {
includes(networkModule, storageModule)
single<UserRepository>()
}

startKoin {
modules(dataModule)
}

Prefer a tree of includes() with one root module over a long flat list in startKoin. The structure of your modules then follows the structure of your code, and the start call stays short.

Koin flattens the tree and loads each module once, even if several parents include it:

val commonModule = module {
single<Logger>()
}

val featureAModule = module {
includes(commonModule)
single<FeatureA>()
}

val featureBModule = module {
includes(commonModule)
single<FeatureB>()
}

val appModule = module {
includes(featureAModule, featureBModule) // commonModule loads once
}

Visibility in multi-module projects​

In a Gradle multi-module project, expose one public module per feature and keep the others private or internal:

// Gradle module "feature-user"
private val userDataModule = module {
single<UserDao>()
single<UserCache>()
}

val userFeatureModule = module {
includes(userDataModule)
viewModel<UserViewModel>()
}

The app module loads userFeatureModule and never sees the internal modules. For a full layout of an Android app split in Gradle modules, see Multi-Module Apps.

Scan packages with @ComponentScan​

@ComponentScan is for annotations only. It collects every annotated class and top-level function in a package and its subpackages:

@Module
@ComponentScan
class AppModule // scans the package of AppModule

@Module
@ComponentScan("com.myapp.features")
class FeatureModule

@Module
@ComponentScan("com.myapp.data", "com.myapp.network")
class InfraModule

With no value, it scans the package of the annotated class. The scan crosses Gradle modules: classes from another Gradle module in the same package are found too.

With the DSL styles, you list each definition in the module yourself. To mix both, include a DSL module in your start block next to the annotated ones (see Typed startup with @KoinApplication).

Auto-discovery with @Configuration​

@Configuration marks a @Module so that @KoinApplication finds it without listing it. The module can live in any Gradle module on the classpath:

// Gradle module "feature-user"
@Module
@Configuration
@ComponentScan("com.myapp.feature.user")
class UserFeatureModule

// Gradle module "app"
@KoinApplication
class MyApp

fun main() {
startKoin<MyApp>() // loads UserFeatureModule
}

Adding a feature then means adding the Gradle dependency: no change to the app's module list. The cost is that the list of loaded modules is no longer written in one place. If you want it explicit, list the modules in @KoinApplication(modules = [...]) instead.

@Configuration takes labels, so you can load different modules per environment or flavor. The rules are on Configuration labels.

@Configuration is annotations only and needs @KoinApplication with typed startup (startKoin<MyApp>()).

Load order and overrides​

When two loaded modules declare the same type, the module loaded last wins. In a DSL start block, this is the order of modules(...):

startKoin {
modules(productionModule, debugModule) // debugModule's ApiService wins
}

With @KoinApplication, the Compiler Plugin builds the module list in this order:

  1. The @Configuration modules it discovers, from your code and from your dependencies.
  2. The modules listed in @KoinApplication(modules = [...]), in declaration order.

So the modules of your app override the defaults of a library:

// In a library
@Module
@Configuration
class CoreModule {
@Singleton
fun feature(): Feature = DefaultFeature()
}

// In the app
@Module
class AppModule {
@Singleton
fun feature(): Feature = AppFeature()
}

@KoinApplication(modules = [AppModule::class])
class MyApp // get<Feature>() returns AppFeature

Inside the explicit list, modules = [A::class, B::class, C::class] loads A, then B, then C, and each module's includes stay grouped with it. A module that is both listed and discovered by @Configuration loads once, at its position in the explicit list. To control the order between several @Configuration modules, list them explicitly.

To make accidental overrides fail at start, use allowOverride(false). The rules for overriding a single definition are in Overriding a definition.

Eager modules​

Mark a whole module to create all its single definitions when Koin starts:

val coreModule = module(createdAtStart = true) {
single<ConfigManager>()
single<CrashReporter>()
}

Each eager definition adds to startup time. See Eager creation for the per-definition flag and its cost.

Parameterized modules​

A DSL module is a value, so a function can build it from arguments:

fun loggingModule(debug: Boolean) = module {
if (debug) {
single<DebugLogger>() bind Logger::class
} else {
single<ReleaseLogger>() bind Logger::class
}
}

startKoin {
modules(loggingModule(debug = BuildConfig.DEBUG))
}

The same idea swaps implementations: keep repositoryModule fixed, and load either localDatasourceModule or remoteDatasourceModule next to it, each declaring a Datasource. Conditional module lists are resolved at runtime, so the Compiler Plugin can't verify every combination at build time.