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

qualifiers-parameters.mddocs/

0

# Qualifiers & Parameters

1

2

This document covers how to distinguish between similar dependencies using qualifiers and how to pass runtime parameters during dependency resolution.

3

4

## Overview

5

6

Koin provides two key mechanisms for handling complex dependency scenarios:

7

- **Qualifiers** - Distinguish between different instances of the same type

8

- **Parameters** - Pass runtime values during dependency resolution

9

10

These features enable sophisticated dependency injection patterns while maintaining type safety and clarity.

11

12

## Qualifier System

13

14

Qualifiers help distinguish between multiple definitions of the same type, enabling you to have different implementations or configurations for different contexts.

15

16

### Qualifier Interface

17

18

```kotlin { .api }

19

interface Qualifier {

20

val value: QualifierValue

21

}

22

23

typealias QualifierValue = String

24

```

25

26

### Qualifier Creation Functions

27

28

```kotlin { .api }

29

// String-based qualifiers

30

fun named(name: String): StringQualifier

31

fun qualifier(name: String): StringQualifier

32

fun _q(name: String): StringQualifier

33

34

// Enum-based qualifiers

35

fun <E : Enum<E>> named(enum: Enum<E>): Qualifier

36

fun <E : Enum<E>> qualifier(enum: Enum<E>): Qualifier

37

val <E : Enum<E>> Enum<E>.qualifier: Qualifier

38

39

// Type-based qualifiers

40

inline fun <reified T> named(): TypeQualifier

41

inline fun <reified T> qualifier(): TypeQualifier

42

inline fun <reified T> _q(): TypeQualifier

43

```

44

45

### Qualifier Implementations

46

47

```kotlin { .api }

48

data class StringQualifier(override val value: QualifierValue) : Qualifier

49

50

class TypeQualifier(val type: KClass<*>) : Qualifier {

51

override val value: QualifierValue

52

}

53

```

54

55

## Using String Qualifiers

56

57

### Basic String Qualifiers

58

59

```kotlin

60

import org.koin.core.qualifier.named

61

import org.koin.dsl.*

62

63

val networkModule = module {

64

// Different HTTP clients for different purposes

65

single<HttpClient>(named("public")) {

66

HttpClient().apply {

67

timeout = 30000

68

}

69

}

70

71

single<HttpClient>(named("authenticated")) {

72

HttpClient().apply {

73

timeout = 60000

74

addAuthInterceptor()

75

}

76

}

77

78

single<HttpClient>(named("admin")) {

79

HttpClient().apply {

80

timeout = 120000

81

addAuthInterceptor()

82

addAdminHeaders()

83

}

84

}

85

}

86

87

// Resolution with qualifiers

88

val koin = koinApplication { modules(networkModule) }.koin

89

90

val publicClient: HttpClient = koin.get(named("public"))

91

val authClient: HttpClient = koin.get(named("authenticated"))

92

val adminClient: HttpClient = koin.get(named("admin"))

93

```

94

95

### Environment-Based Qualifiers

96

97

```kotlin

98

val configModule = module {

99

single<DatabaseConfig>(named("development")) {

100

DatabaseConfig("jdbc:h2:mem:dev", "dev_user", "dev_pass")

101

}

102

103

single<DatabaseConfig>(named("production")) {

104

DatabaseConfig("jdbc:postgresql://prod-server/db", "prod_user", "prod_pass")

105

}

106

107

single<DatabaseConfig>(named("test")) {

108

DatabaseConfig("jdbc:h2:mem:test", "test_user", "test_pass")

109

}

110

}

111

112

class DatabaseService : KoinComponent {

113

private val environment = System.getenv("ENVIRONMENT") ?: "development"

114

private val config: DatabaseConfig = get(named(environment))

115

}

116

```

117

118

### Service Variant Qualifiers

119

120

```kotlin

121

val serviceModule = module {

122

// Different implementations of the same service

123

single<PaymentService>(named("stripe")) { StripePaymentService() }

124

single<PaymentService>(named("paypal")) { PayPalPaymentService() }

125

single<PaymentService>(named("mock")) { MockPaymentService() }

126

127

// Email service variants

128

single<EmailService>(named("smtp")) { SMTPEmailService() }

129

single<EmailService>(named("sendgrid")) { SendGridEmailService() }

130

single<EmailService>(named("console")) { ConsoleEmailService() }

131

}

132

```

133

134

## Using Enum Qualifiers

135

136

### Enum-Based Qualifiers

137

138

```kotlin

139

enum class ServiceTier {

140

FREE, PREMIUM, ENTERPRISE

141

}

142

143

enum class Region {

144

US_EAST, US_WEST, EUROPE, ASIA

145

}

146

147

val tierModule = module {

148

// Using enum qualifiers

149

single<FeatureSet>(ServiceTier.FREE.qualifier) {

150

FreeFeatureSet()

151

}

152

153

single<FeatureSet>(ServiceTier.PREMIUM.qualifier) {

154

PremiumFeatureSet()

155

}

156

157

single<FeatureSet>(ServiceTier.ENTERPRISE.qualifier) {

158

EnterpriseFeatureSet()

159

}

160

161

// Regional services

162

single<RegionalService>(Region.US_EAST.qualifier) {

163

USEastService()

164

}

165

166

single<RegionalService>(Region.EUROPE.qualifier) {

167

EuropeService()

168

}

169

}

170

171

// Usage with enum qualifiers

172

class TieredService : KoinComponent {

173

fun getFeatureSet(tier: ServiceTier): FeatureSet {

174

return get(tier.qualifier)

175

}

176

177

fun getRegionalService(region: Region): RegionalService {

178

return get(named(region)) // Alternative syntax

179

}

180

}

181

```

182

183

## Using Type Qualifiers

184

185

### Type-Based Qualifiers

186

187

```kotlin

188

// Marker types for different contexts

189

class ProductionContext

190

class DevelopmentContext

191

class TestContext

192

193

val contextModule = module {

194

// Type-based qualifiers

195

single<Logger>(named<ProductionContext>()) {

196

FileLogger("/var/log/app.log")

197

}

198

199

single<Logger>(named<DevelopmentContext>()) {

200

ConsoleLogger()

201

}

202

203

single<Logger>(named<TestContext>()) {

204

MockLogger()

205

}

206

207

// Cache implementations by type

208

single<Cache>(qualifier<UserCache>()) {

209

RedisCache("user-cache")

210

}

211

212

single<Cache>(qualifier<SessionCache>()) {

213

InMemoryCache()

214

}

215

}

216

217

// Usage

218

class ContextAwareService : KoinComponent {

219

private val prodLogger: Logger = get(named<ProductionContext>())

220

private val userCache: Cache = get(qualifier<UserCache>())

221

}

222

```

223

224

### Generic Type Qualifiers

225

226

```kotlin

227

// Different cache instances for different data types

228

val cacheModule = module {

229

single<Cache<User>>(named("user")) {

230

InMemoryCache<User>()

231

}

232

233

single<Cache<Product>>(named("product")) {

234

RedisCache<Product>("products")

235

}

236

237

single<Cache<String>>(named("session")) {

238

InMemoryCache<String>()

239

}

240

}

241

```

242

243

## Parameter System

244

245

Parameters allow you to pass runtime values during dependency resolution, enabling dynamic configuration and context-specific instantiation.

246

247

### ParametersHolder Class

248

249

```kotlin { .api }

250

class ParametersHolder(

251

internal val _values: MutableList<Any?> = mutableListOf(),

252

val useIndexedValues: Boolean? = null

253

) {

254

val values: List<Any?>

255

var index: Int

256

257

// Access by index

258

operator fun <T> get(i: Int): T

259

fun <T> set(i: Int, t: T)

260

261

// Access by type

262

inline fun <reified T : Any> get(): T

263

inline fun <reified T : Any> getOrNull(): T?

264

fun <T> getOrNull(clazz: KClass<*>): T?

265

fun <T> elementAt(i: Int, clazz: KClass<*>): T

266

267

// Destructuring components

268

inline operator fun <reified T> component1(): T

269

inline operator fun <reified T> component2(): T

270

inline operator fun <reified T> component3(): T

271

inline operator fun <reified T> component4(): T

272

inline operator fun <reified T> component5(): T

273

274

// Utilities

275

fun size(): Int

276

fun isEmpty(): Boolean

277

fun isNotEmpty(): Boolean

278

fun add(value: Any): ParametersHolder

279

fun insert(index: Int, value: Any): ParametersHolder

280

}

281

282

typealias ParametersDefinition = () -> ParametersHolder

283

```

284

285

### Parameter Creation Functions

286

287

```kotlin { .api }

288

// Standard parameters - indexed or type-based resolution

289

fun parametersOf(vararg parameters: Any?): ParametersHolder

290

291

// Array-based parameters - consumed one by one (indexed)

292

fun parameterArrayOf(vararg parameters: Any?): ParametersHolder

293

294

// Set-based parameters - different types of values

295

fun parameterSetOf(vararg parameters: Any?): ParametersHolder

296

297

// Empty parameters

298

fun emptyParametersHolder(): ParametersHolder

299

```

300

301

## Working with Parameters

302

303

### Basic Parameter Usage

304

305

```kotlin

306

val parameterModule = module {

307

factory<DatabaseConnection> { params ->

308

val host: String = params.get()

309

val port: Int = params.get()

310

val database: String = params.get()

311

DatabaseConnection(host, port, database)

312

}

313

}

314

315

val koin = koinApplication { modules(parameterModule) }.koin

316

317

// Pass parameters during resolution

318

val connection: DatabaseConnection = koin.get {

319

parametersOf("localhost", 5432, "myapp")

320

}

321

```

322

323

### Indexed Parameter Access

324

325

```kotlin

326

val indexedModule = module {

327

factory<ApiClient> { params ->

328

val baseUrl: String = params[0] // Get by index

329

val timeout: Int = params[1]

330

val apiKey: String = params[2]

331

ApiClient(baseUrl, timeout, apiKey)

332

}

333

}

334

335

// Usage

336

val apiClient: ApiClient = koin.get {

337

parametersOf("https://api.example.com", 30000, "secret-key")

338

}

339

```

340

341

### Type-Based Parameter Access

342

343

```kotlin

344

val typedModule = module {

345

factory<EmailService> { params ->

346

val config: EmailConfig = params.get() // Get by type

347

val logger: Logger = params.get()

348

val retries: Int = params.get()

349

EmailService(config, logger, retries)

350

}

351

}

352

353

// Usage with different types

354

val emailService: EmailService = koin.get {

355

parametersOf(

356

EmailConfig("smtp.example.com"),

357

FileLogger("/tmp/email.log"),

358

3

359

)

360

}

361

```

362

363

### Destructuring Parameters

364

365

```kotlin

366

val destructuringModule = module {

367

factory<ServiceConfig> { params ->

368

val (host: String, port: Int, ssl: Boolean) = params

369

ServiceConfig(host, port, ssl)

370

}

371

}

372

373

// Usage

374

val config: ServiceConfig = koin.get {

375

parametersOf("api.example.com", 443, true)

376

}

377

```

378

379

### Parameter Access Modes

380

381

```kotlin

382

val parameterModule = module {

383

factory<MultiTypeService> { params ->

384

// Mixed parameter resolution - by index or by type

385

val value1: String = params.get() // By type

386

val value2: Int = params.get() // By type

387

MultiTypeService(value1, value2.toString())

388

}

389

}

390

391

// Regular parameters

392

val service: MultiTypeService = koin.get {

393

parametersOf("value", 42)

394

}

395

```

396

397

## Combining Qualifiers and Parameters

398

399

### Qualified Parameterized Definitions

400

401

```kotlin

402

val combinedModule = module {

403

// Different database connections with parameters

404

factory<DatabaseConnection>(named("primary")) { params ->

405

val credentials: Credentials = params.get()

406

PrimaryDatabaseConnection(credentials)

407

}

408

409

factory<DatabaseConnection>(named("backup")) { params ->

410

val credentials: Credentials = params.get()

411

val timeout: Int = params.get()

412

BackupDatabaseConnection(credentials, timeout)

413

}

414

415

factory<DatabaseConnection>(named("readonly")) { params ->

416

val (host: String, database: String) = params

417

ReadOnlyDatabaseConnection(host, database)

418

}

419

}

420

421

// Usage with both qualifiers and parameters

422

val primary: DatabaseConnection = koin.get(named("primary")) {

423

parametersOf(Credentials("user", "pass"))

424

}

425

426

val backup: DatabaseConnection = koin.get(named("backup")) {

427

parametersOf(Credentials("backup_user", "backup_pass"), 60000)

428

}

429

430

val readonly: DatabaseConnection = koin.get(named("readonly")) {

431

parametersOf("replica.db.com", "myapp_readonly")

432

}

433

```

434

435

### Dynamic Qualifier Selection

436

437

```kotlin

438

val dynamicModule = module {

439

single<CacheService>(named("redis")) { params ->

440

val config: RedisConfig = params.get()

441

RedisCacheService(config)

442

}

443

444

single<CacheService>(named("memory")) { params ->

445

val maxSize: Int = params.getOrNull() ?: 1000

446

InMemoryCacheService(maxSize)

447

}

448

}

449

450

class CacheManager : KoinComponent {

451

fun getCacheService(type: String, config: Any? = null): CacheService {

452

return when (type) {

453

"redis" -> get(named("redis")) {

454

parametersOf(config as RedisConfig)

455

}

456

"memory" -> get(named("memory")) {

457

config?.let { parametersOf(it) } ?: emptyParametersHolder()

458

}

459

else -> throw IllegalArgumentException("Unknown cache type: $type")

460

}

461

}

462

}

463

```

464

465

## Advanced Patterns

466

467

### Factory Functions with Qualifiers

468

469

```kotlin

470

class ServiceFactory : KoinComponent {

471

fun createUserService(userId: String, tier: ServiceTier): UserService {

472

return get(tier.qualifier) { parametersOf(userId) }

473

}

474

475

fun createRegionalService(region: Region, config: RegionalConfig): RegionalService {

476

return get(region.qualifier) { parametersOf(config) }

477

}

478

}

479

480

val factoryModule = module {

481

factory<UserService>(ServiceTier.FREE.qualifier) { params ->

482

val userId: String = params.get()

483

FreeUserService(userId)

484

}

485

486

factory<UserService>(ServiceTier.PREMIUM.qualifier) { params ->

487

val userId: String = params.get()

488

PremiumUserService(userId, get<AnalyticsService>())

489

}

490

}

491

```

492

493

### Conditional Parameter Handling

494

495

```kotlin

496

val conditionalModule = module {

497

factory<ConfigurableService> { params ->

498

val config: ServiceConfig = params.get()

499

val logger: Logger? = params.getOrNull()

500

val metrics: MetricsService? = params.getOrNull()

501

502

ConfigurableService(config).apply {

503

logger?.let { setLogger(it) }

504

metrics?.let { setMetrics(it) }

505

}

506

}

507

}

508

509

// Usage with optional parameters

510

val basicService: ConfigurableService = koin.get {

511

parametersOf(ServiceConfig("basic"))

512

}

513

514

val advancedService: ConfigurableService = koin.get {

515

parametersOf(

516

ServiceConfig("advanced"),

517

FileLogger("service.log"),

518

PrometheusMetrics()

519

)

520

}

521

```

522

523

### Parameter Validation

524

525

```kotlin

526

val validationModule = module {

527

factory<ValidatedService> { params ->

528

val config: ServiceConfig = params.get()

529

val timeout: Int = params.get()

530

531

require(timeout > 0) { "Timeout must be positive" }

532

require(config.isValid()) { "Invalid service configuration" }

533

534

ValidatedService(config, timeout)

535

}

536

}

537

```

538

539

## Best Practices

540

541

### 1. Use Meaningful Qualifier Names

542

543

```kotlin

544

// Good - descriptive names

545

single<HttpClient>(named("user-api-client")) { /* ... */ }

546

single<Database>(named("primary-user-db")) { /* ... */ }

547

548

// Avoid - generic names

549

single<HttpClient>(named("client1")) { /* ... */ }

550

single<Database>(named("db")) { /* ... */ }

551

```

552

553

### 2. Consistent Qualifier Patterns

554

555

```kotlin

556

// Consistent naming pattern

557

val apiModule = module {

558

single<ApiService>(named("user-api")) { /* ... */ }

559

single<ApiService>(named("product-api")) { /* ... */ }

560

single<ApiService>(named("order-api")) { /* ... */ }

561

}

562

```

563

564

### 3. Parameter Type Safety

565

566

```kotlin

567

// Good - explicit type handling

568

factory<Service> { params ->

569

val config: ServiceConfig = params.get()

570

val timeout: Int = params.get()

571

Service(config, timeout)

572

}

573

574

// Better - with validation

575

factory<Service> { params ->

576

val config: ServiceConfig = params.getOrNull()

577

?: throw IllegalArgumentException("ServiceConfig required")

578

val timeout: Int = params.getOrNull() ?: DEFAULT_TIMEOUT

579

Service(config, timeout)

580

}

581

```

582

583

### 4. Organize Qualifiers

584

585

```kotlin

586

object Qualifiers {

587

val PRIMARY_DB = named("primary-database")

588

val BACKUP_DB = named("backup-database")

589

val CACHE_DB = named("cache-database")

590

591

val USER_API = named("user-api-client")

592

val ADMIN_API = named("admin-api-client")

593

}

594

595

val module = module {

596

single<Database>(Qualifiers.PRIMARY_DB) { /* ... */ }

597

single<ApiClient>(Qualifiers.USER_API) { /* ... */ }

598

}

599

```

600

601

Qualifiers and parameters provide powerful mechanisms for handling complex dependency injection scenarios while maintaining clean, type-safe code. They enable flexible, context-aware dependency resolution that scales with application complexity.