0
# Module System & Definitions
1
2
This document covers how to organize your dependencies using Koin's module system and define different types of components with the DSL.
3
4
## Overview
5
6
Koin modules are containers that organize your dependency definitions. The module DSL provides several definition types:
7
- **Singletons** (`single`) - One instance shared across the application
8
- **Factories** (`factory`) - New instance created on each request
9
- **Scoped instances** (`scoped`) - Instance tied to a specific scope lifecycle
10
11
Modules can be composed, combined, and loaded dynamically to build flexible dependency injection architectures.
12
13
## Module Creation
14
15
### Basic Module Definition
16
17
```kotlin { .api }
18
import org.koin.dsl.module
19
20
fun module(createdAtStart: Boolean = false, moduleDeclaration: ModuleDeclaration): Module
21
22
typealias ModuleDeclaration = Module.() -> Unit
23
```
24
25
#### Simple Module Example
26
27
```kotlin
28
import org.koin.dsl.*
29
30
val appModule = module {
31
single<Repository> { DatabaseRepository() }
32
factory<UseCase> { GetDataUseCase(get()) }
33
}
34
```
35
36
#### Module with Eager Initialization
37
38
```kotlin
39
val databaseModule = module(createdAtStart = true) {
40
single<Database> { DatabaseConnection() }
41
single<Migrator> { DatabaseMigrator(get()) }
42
}
43
```
44
45
## Module Class
46
47
The `Module` class provides the container and DSL for dependency definitions:
48
49
```kotlin { .api }
50
class Module(@PublishedApi internal val _createdAtStart: Boolean = false) {
51
val id: String
52
val isLoaded: Boolean
53
var eagerInstances: LinkedHashSet<SingleInstanceFactory<*>>
54
55
// Core definition methods
56
inline fun <reified T> single(
57
qualifier: Qualifier? = null,
58
createdAtStart: Boolean = false,
59
noinline definition: Definition<T>
60
): KoinDefinition<T>
61
62
inline fun <reified T> factory(
63
qualifier: Qualifier? = null,
64
noinline definition: Definition<T>
65
): KoinDefinition<T>
66
67
// Scope definitions
68
fun scope(qualifier: Qualifier, scopeSet: ScopeDSL.() -> Unit)
69
inline fun <reified T> scope(scopeSet: ScopeDSL.() -> Unit)
70
71
// Module composition
72
fun includes(vararg module: Module)
73
fun includes(module: Collection<Module>)
74
operator fun plus(module: Module) = listOf(this, module)
75
operator fun plus(modules: List<Module>) = listOf(this) + modules
76
}
77
78
// Type aliases
79
typealias Definition<T> = Scope.(ParametersHolder) -> T
80
```
81
82
## Singleton Definitions
83
84
Singletons are created once and reused throughout the application lifecycle.
85
86
### Basic Singleton
87
88
```kotlin
89
val singletonModule = module {
90
// Simple singleton
91
single<Logger> { FileLogger() }
92
93
// Singleton with dependencies
94
single<UserService> { UserService(get(), get()) }
95
}
96
```
97
98
### Singleton with Qualifier
99
100
```kotlin
101
import org.koin.core.qualifier.named
102
103
val configModule = module {
104
single<Config>(named("dev")) { DevConfig() }
105
single<Config>(named("prod")) { ProductionConfig() }
106
}
107
```
108
109
### Eager Singletons
110
111
```kotlin
112
val eagerModule = module {
113
// Eager singleton - created at startup
114
single<DatabaseConnection>(createdAtStart = true) {
115
DatabaseConnection()
116
}
117
118
// Regular singleton - created when first requested
119
single<UserRepository> { UserRepository(get()) }
120
}
121
```
122
123
### Singleton with Parameters
124
125
```kotlin
126
val parameterizedModule = module {
127
single<ApiClient> { params ->
128
val baseUrl: String = params.get()
129
val timeout: Int = params.get()
130
ApiClient(baseUrl, timeout)
131
}
132
}
133
134
// Usage:
135
val apiClient = koin.get<ApiClient> {
136
parametersOf("https://api.example.com", 30000)
137
}
138
```
139
140
## Factory Definitions
141
142
Factories create a new instance each time they're requested.
143
144
### Basic Factory
145
146
```kotlin
147
val factoryModule = module {
148
// New instance on each get()
149
factory<RequestHandler> { RequestHandler() }
150
151
// Factory with dependencies
152
factory<EmailService> { EmailService(get(), get()) }
153
}
154
```
155
156
### Factory vs Singleton Comparison
157
158
```kotlin
159
val comparisonModule = module {
160
single<DatabaseConnection> { DatabaseConnection() } // Shared
161
factory<Transaction> { Transaction(get()) } // New each time
162
}
163
164
// Usage demonstrates difference:
165
val conn1 = koin.get<DatabaseConnection>() // Same instance
166
val conn2 = koin.get<DatabaseConnection>() // Same instance (conn1 === conn2)
167
168
val tx1 = koin.get<Transaction>() // New instance
169
val tx2 = koin.get<Transaction>() // New instance (tx1 !== tx2)
170
```
171
172
### Factory with Complex Logic
173
174
```kotlin
175
val complexModule = module {
176
factory<ReportGenerator> { params ->
177
val reportType: String = params.get()
178
val format: String = params.get()
179
180
when (reportType) {
181
"sales" -> SalesReportGenerator(format, get())
182
"inventory" -> InventoryReportGenerator(format, get())
183
else -> DefaultReportGenerator(format)
184
}
185
}
186
}
187
```
188
189
## Definition Binding
190
191
Bind definitions to additional types for polymorphic resolution:
192
193
```kotlin { .api }
194
import org.koin.dsl.*
195
196
// Bind to additional class
197
infix fun <S : Any> KoinDefinition<out S>.bind(clazz: KClass<S>): KoinDefinition<out S>
198
199
// Reified binding
200
inline fun <reified S : Any> KoinDefinition<out S>.bind(): KoinDefinition<out S>
201
202
// Bind to multiple classes
203
infix fun KoinDefinition<*>.binds(classes: Array<KClass<*>>): KoinDefinition<*>
204
205
// Close callback
206
infix fun <T> KoinDefinition<T>.onClose(onClose: OnCloseCallback<T>): KoinDefinition<T>
207
```
208
209
### Interface Binding Examples
210
211
```kotlin
212
interface Repository
213
interface ReadOnlyRepository
214
class DatabaseRepository : Repository, ReadOnlyRepository
215
216
val repositoryModule = module {
217
// Bind to multiple interfaces
218
single<DatabaseRepository> { DatabaseRepository() }
219
.bind<Repository>()
220
.bind<ReadOnlyRepository>()
221
222
// Alternative syntax
223
single<DatabaseRepository> { DatabaseRepository() }
224
.binds(arrayOf(Repository::class, ReadOnlyRepository::class))
225
}
226
227
// Can resolve as any bound type:
228
val db: DatabaseRepository = koin.get()
229
val repo: Repository = koin.get() // Same instance as db
230
val readOnly: ReadOnlyRepository = koin.get() // Same instance as db
231
```
232
233
### Cleanup Callbacks
234
235
```kotlin
236
val resourceModule = module {
237
single<FileResource> { FileResource("/tmp/data") }
238
.onClose { resource ->
239
resource.cleanup()
240
println("Resource cleaned up")
241
}
242
}
243
```
244
245
## Constructor-Based Definitions
246
247
Koin provides constructor-reference DSL for compile-time type safety:
248
249
### SingleOf Functions
250
251
```kotlin { .api }
252
import org.koin.core.module.dsl.*
253
254
// Zero-argument constructor
255
inline fun <reified R> Module.singleOf(
256
crossinline constructor: () -> R,
257
noinline options: DefinitionOptions<R>? = null
258
): KoinDefinition<R>
259
260
// Constructors with 1-22 parameters
261
inline fun <reified R, reified T1> Module.singleOf(
262
crossinline constructor: (T1) -> R,
263
noinline options: DefinitionOptions<R>? = null
264
): KoinDefinition<R>
265
266
// ... up to T1, T2, ..., T22
267
```
268
269
### FactoryOf Functions
270
271
Similar pattern for factory definitions:
272
273
```kotlin { .api }
274
inline fun <reified R> Module.factoryOf(
275
crossinline constructor: () -> R,
276
noinline options: DefinitionOptions<R>? = null
277
): KoinDefinition<R>
278
279
// ... with parameters
280
```
281
282
### Constructor DSL Examples
283
284
```kotlin
285
class UserService(private val repo: UserRepository, private val logger: Logger)
286
class UserRepository(private val database: Database)
287
class Logger()
288
289
val constructorModule = module {
290
// Using constructor references
291
singleOf(::Logger)
292
singleOf(::UserRepository) // Automatically resolves Database dependency
293
singleOf(::UserService) // Resolves UserRepository and Logger
294
295
// Equivalent traditional syntax:
296
// single<Logger> { Logger() }
297
// single<UserRepository> { UserRepository(get()) }
298
// single<UserService> { UserService(get(), get()) }
299
}
300
```
301
302
### Constructor Options
303
304
```kotlin
305
val optionsModule = module {
306
singleOf(::DatabaseConnection) { options ->
307
options.createdAtStart()
308
options.onClose { it.disconnect() }
309
}
310
311
factoryOf(::HttpClient) { options ->
312
options.bind<Client>()
313
}
314
}
315
```
316
317
## Scoped Definitions
318
319
Scoped definitions are covered in detail in [Scope Management](scoping.md), but here's an overview:
320
321
### Scope Definition DSL
322
323
```kotlin { .api }
324
class ScopeDSL(val scopeQualifier: Qualifier, val module: Module) {
325
inline fun <reified T> scoped(
326
qualifier: Qualifier? = null,
327
noinline definition: Definition<T>
328
): KoinDefinition<T>
329
330
inline fun <reified T> factory(
331
qualifier: Qualifier? = null,
332
noinline definition: Definition<T>
333
): KoinDefinition<T>
334
}
335
```
336
337
### Basic Scope Usage
338
339
```kotlin
340
import org.koin.core.qualifier.named
341
342
val scopedModule = module {
343
scope(named("session")) {
344
scoped<UserSession> { UserSession() }
345
scoped<ShoppingCart> { ShoppingCart(get()) }
346
factory<TempData> { TempData() } // New instance each time within scope
347
}
348
}
349
```
350
351
### Type-Based Scopes
352
353
```kotlin
354
class UserSession
355
356
val typedScopeModule = module {
357
scope<UserSession> {
358
scoped<UserPreferences> { UserPreferences() }
359
scoped<UserCache> { UserCache() }
360
}
361
}
362
```
363
364
## Module Composition
365
366
### Including Modules
367
368
```kotlin
369
val databaseModule = module {
370
single<Database> { Database() }
371
}
372
373
val repositoryModule = module {
374
single<UserRepository> { UserRepository(get()) }
375
}
376
377
val appModule = module {
378
// Include other modules
379
includes(databaseModule, repositoryModule)
380
381
// Add own definitions
382
single<UserService> { UserService(get()) }
383
}
384
```
385
386
### Module Combination
387
388
```kotlin
389
val coreModules = databaseModule + repositoryModule
390
val allModules = coreModules + appModule
391
392
// Load combined modules
393
val app = koinApplication {
394
modules(allModules)
395
}
396
```
397
398
### Conditional Module Inclusion
399
400
```kotlin
401
fun createAppModule(isProduction: Boolean): Module = module {
402
includes(coreModule)
403
404
if (isProduction) {
405
includes(productionModule)
406
} else {
407
includes(developmentModule)
408
}
409
}
410
```
411
412
## Advanced Definition Patterns
413
414
### Conditional Definitions
415
416
```kotlin
417
val environmentModule = module {
418
single<Config> {
419
val env = System.getenv("ENVIRONMENT") ?: "development"
420
when (env) {
421
"production" -> ProductionConfig()
422
"test" -> TestConfig()
423
else -> DevelopmentConfig()
424
}
425
}
426
}
427
```
428
429
### Definition Factories
430
431
```kotlin
432
fun createServiceModule(apiKey: String): Module = module {
433
single<ApiService> { ApiService(apiKey) }
434
factory<ApiClient> { ApiClient(get()) }
435
}
436
437
// Usage:
438
val serviceModule = createServiceModule("prod-key-123")
439
```
440
441
### Generic Type Handling
442
443
```kotlin
444
interface Cache<T>
445
class InMemoryCache<T> : Cache<T>
446
447
val cacheModule = module {
448
single<Cache<String>> { InMemoryCache<String>() }
449
single<Cache<Int>> { InMemoryCache<Int>() }
450
single<Cache<User>> { InMemoryCache<User>() }
451
}
452
```
453
454
## Definition Options & DSL
455
456
### Option DSL
457
458
```kotlin { .api }
459
import org.koin.core.module.dsl.OptionDSL
460
461
class OptionDSL<T> {
462
fun createdAtStart()
463
fun onClose(callback: OnCloseCallback<T>)
464
}
465
```
466
467
### Using Options
468
469
```kotlin
470
val optionModule = module {
471
single<ConnectionPool> { ConnectionPool() } onClose { pool ->
472
pool.shutdown()
473
}
474
475
single<Cache> { RedisCache() } bind Cache::class
476
}
477
```
478
479
## Best Practices
480
481
### 1. Organize by Feature
482
483
```kotlin
484
// User feature module
485
val userModule = module {
486
single<UserRepository> { UserRepository(get()) }
487
factory<UserService> { UserService(get()) }
488
}
489
490
// Payment feature module
491
val paymentModule = module {
492
single<PaymentGateway> { PaymentGateway(get()) }
493
factory<PaymentProcessor> { PaymentProcessor(get()) }
494
}
495
```
496
497
### 2. Use Constructor References
498
499
```kotlin
500
// Preferred - type-safe at compile time
501
val preferredModule = module {
502
singleOf(::UserService)
503
factoryOf(::EmailService)
504
}
505
506
// Avoid - runtime resolution
507
val avoidModule = module {
508
single<UserService> { UserService(get(), get()) }
509
}
510
```
511
512
### 3. Separate Configuration from Logic
513
514
```kotlin
515
// Configuration module
516
val configModule = module {
517
single<DatabaseUrl> { DatabaseUrl("jdbc:postgresql://localhost/app") }
518
single<ApiTimeout> { ApiTimeout(30000) }
519
}
520
521
// Service module using configuration
522
val serviceModule = module {
523
single<DatabaseService> { DatabaseService(get<DatabaseUrl>().value) }
524
single<ApiService> { ApiService(get<ApiTimeout>().value) }
525
}
526
```
527
528
### 4. Use Qualifiers for Variants
529
530
```kotlin
531
import org.koin.core.qualifier.named
532
533
val multiVariantModule = module {
534
single<HttpClient>(named("authenticated")) {
535
HttpClient().apply {
536
addAuthInterceptor()
537
}
538
}
539
single<HttpClient>(named("public")) {
540
HttpClient()
541
}
542
}
543
```
544
545
This module system provides a clean, flexible way to organize dependencies while maintaining clear separation of concerns and supporting various architectural patterns.