or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application-setup.mdcomponents.mddependency-injection.mderror-handling.mdindex.mdmodules-and-definitions.mdqualifiers-parameters.mdscoping.md

modules-and-definitions.mddocs/

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.