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

components.mddocs/

0

# Component Integration

1

2

This document covers how to integrate dependency injection into your classes using Koin's component interfaces and extension functions.

3

4

## Overview

5

6

Koin provides component interfaces that enable seamless dependency injection integration:

7

- **KoinComponent** - Basic dependency injection capabilities

8

- **KoinScopeComponent** - Scope-aware dependency injection with lifecycle management

9

10

These interfaces provide extension functions that allow your classes to directly access the dependency injection container without explicitly passing around Koin instances.

11

12

## KoinComponent Interface

13

14

The basic interface for dependency injection integration:

15

16

```kotlin { .api }

17

interface KoinComponent {

18

fun getKoin(): Koin

19

}

20

```

21

22

### Extension Functions

23

24

```kotlin { .api }

25

// Direct resolution

26

inline fun <reified T : Any> KoinComponent.get(

27

qualifier: Qualifier? = null,

28

noinline parameters: ParametersDefinition? = null

29

): T

30

31

// Lazy injection

32

inline fun <reified T : Any> KoinComponent.inject(

33

qualifier: Qualifier? = null,

34

mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(),

35

noinline parameters: ParametersDefinition? = null

36

): Lazy<T>

37

```

38

39

### Basic Implementation

40

41

```kotlin

42

import org.koin.core.component.KoinComponent

43

import org.koin.core.component.get

44

import org.koin.core.component.inject

45

46

class UserService : KoinComponent {

47

// Direct injection - resolved immediately

48

private val repository: UserRepository = get()

49

50

// Lazy injection - resolved when first accessed

51

private val logger: Logger by inject()

52

53

// Qualified injection

54

private val cache: Cache = get(named("user"))

55

56

fun processUser(userId: String) {

57

logger.info("Processing user: $userId")

58

val user = repository.findById(userId)

59

// ... process user

60

}

61

}

62

```

63

64

### With Parameters

65

66

```kotlin

67

import org.koin.core.parameter.parametersOf

68

69

class ConfigurableService : KoinComponent {

70

fun createClient(endpoint: String, timeout: Int): ApiClient {

71

return get { parametersOf(endpoint, timeout) }

72

}

73

74

fun getEnvironmentConfig(): Config {

75

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

76

return get(named(env))

77

}

78

}

79

```

80

81

### Custom Koin Context

82

83

```kotlin

84

class IsolatedService(private val customKoin: Koin) : KoinComponent {

85

// Override to use custom Koin instance

86

override fun getKoin(): Koin = customKoin

87

88

private val service: DataService by inject()

89

}

90

```

91

92

## KoinScopeComponent Interface

93

94

The scope-aware interface that automatically manages scoped dependencies:

95

96

```kotlin { .api }

97

interface KoinScopeComponent : KoinComponent {

98

val scope: Scope

99

}

100

```

101

102

### Extension Functions

103

104

```kotlin { .api }

105

// Scope identification

106

fun <T : Any> T.getScopeId(): String

107

fun <T : Any> T.getScopeName(): TypeQualifier

108

109

// Scope creation

110

fun <T : KoinScopeComponent> T.createScope(

111

scopeId: ScopeID = getScopeId(),

112

source: Any? = null,

113

scopeArchetype: TypeQualifier? = null

114

): Scope

115

116

fun <T : KoinScopeComponent> T.createScope(source: Any? = null): Scope

117

118

// Scope access

119

fun <T : KoinScopeComponent> T.getScopeOrNull(): Scope?

120

121

// Lazy scope creation

122

fun <T : KoinScopeComponent> T.newScope(): Lazy<Scope>

123

fun <T : KoinScopeComponent> T.getOrCreateScope(): Lazy<Scope>

124

```

125

126

### Basic Scope Component

127

128

```kotlin

129

import org.koin.core.component.KoinScopeComponent

130

import org.koin.core.component.createScope

131

import org.koin.core.component.get

132

import org.koin.core.scope.Scope

133

134

class UserSession : KoinScopeComponent {

135

// Automatically creates scope for this instance

136

override val scope: Scope by lazy { createScope() }

137

138

// Scoped dependencies - shared within this session

139

private val sessionData: SessionData by inject()

140

private val preferences: UserPreferences by inject()

141

142

fun login(credentials: Credentials) {

143

// Dependencies are resolved from this instance's scope

144

sessionData.initialize(credentials.userId)

145

preferences.load(credentials.userId)

146

}

147

148

fun logout() {

149

// Close scope when session ends

150

scope.close()

151

}

152

}

153

```

154

155

### Manual Scope Management

156

157

```kotlin

158

class CustomScopeComponent : KoinScopeComponent {

159

private var _scope: Scope? = null

160

161

override val scope: Scope

162

get() = _scope ?: throw IllegalStateException("Scope not initialized")

163

164

fun initialize() {

165

_scope = createScope()

166

}

167

168

fun cleanup() {

169

_scope?.close()

170

_scope = null

171

}

172

}

173

```

174

175

### Scope Resolution Behavior

176

177

When using `KoinScopeComponent`, the `get()` and `inject()` functions automatically use the component's scope:

178

179

```kotlin

180

class ScopedService : KoinScopeComponent {

181

override val scope: Scope by lazy { createScope() }

182

183

fun example() {

184

// These resolve from the component's scope

185

val service1: MyService = get() // scope.get<MyService>()

186

val service2: MyService by inject() // scope.inject<MyService>()

187

188

// Equivalent explicit calls

189

val service3: MyService = scope.get()

190

val service4: MyService by scope.inject()

191

}

192

}

193

```

194

195

## Practical Implementation Patterns

196

197

### Service Layer Integration

198

199

```kotlin

200

// Business service with dependency injection

201

class OrderService : KoinComponent {

202

private val orderRepository: OrderRepository by inject()

203

private val paymentService: PaymentService by inject()

204

private val emailService: EmailService by inject()

205

private val logger: Logger by inject()

206

207

fun processOrder(order: Order): OrderResult {

208

logger.info("Processing order ${order.id}")

209

210

return try {

211

// Validate order

212

val validatedOrder = validateOrder(order)

213

214

// Process payment

215

val payment = paymentService.processPayment(validatedOrder.total)

216

217

// Save order

218

val savedOrder = orderRepository.save(validatedOrder.copy(paymentId = payment.id))

219

220

// Send confirmation

221

emailService.sendOrderConfirmation(savedOrder)

222

223

OrderResult.Success(savedOrder)

224

} catch (e: Exception) {

225

logger.error("Failed to process order ${order.id}", e)

226

OrderResult.Failure(e.message)

227

}

228

}

229

230

private fun validateOrder(order: Order): Order {

231

// Validation logic

232

return order

233

}

234

}

235

```

236

237

### UI Component Integration

238

239

```kotlin

240

// Android ViewModel example

241

class UserViewModel : KoinComponent {

242

private val userService: UserService by inject()

243

private val analyticsService: AnalyticsService by inject(named("firebase"))

244

245

private val _users = MutableLiveData<List<User>>()

246

val users: LiveData<List<User>> = _users

247

248

fun loadUsers() {

249

viewModelScope.launch {

250

try {

251

val userList = userService.getAllUsers()

252

_users.value = userList

253

analyticsService.track("users_loaded", mapOf("count" to userList.size))

254

} catch (e: Exception) {

255

// Handle error

256

}

257

}

258

}

259

}

260

```

261

262

### Scoped Component Lifecycle

263

264

```kotlin

265

class RequestHandler : KoinScopeComponent {

266

// Each request gets its own scope

267

override val scope: Scope by lazy { createScope() }

268

269

private val requestData: RequestData by inject()

270

private val auditLogger: AuditLogger by inject()

271

272

fun handleRequest(request: HttpRequest): HttpResponse {

273

try {

274

// Process with scoped dependencies

275

auditLogger.logRequest(request)

276

277

val result = processRequest(request)

278

279

auditLogger.logResponse(result)

280

return result

281

282

} finally {

283

// Always cleanup scope

284

scope.close()

285

}

286

}

287

288

private fun processRequest(request: HttpRequest): HttpResponse {

289

// Business logic using scoped dependencies

290

return HttpResponse.ok()

291

}

292

}

293

```

294

295

### Factory with Component Integration

296

297

```kotlin

298

class ServiceFactory : KoinComponent {

299

fun createUserService(userId: String): UserService {

300

// Inject dependencies for the service

301

val repository: UserRepository = get()

302

val cache: Cache = get(named("user"))

303

304

return UserService(userId, repository, cache)

305

}

306

307

fun createAdminService(adminLevel: Int): AdminService {

308

return get { parametersOf(adminLevel) }

309

}

310

}

311

```

312

313

## Advanced Component Patterns

314

315

### Component with Multiple Scopes

316

317

```kotlin

318

class MultiScopeComponent : KoinComponent {

319

private val globalKoin = getKoin()

320

321

// Different scopes for different contexts

322

private val sessionScope: Scope by lazy {

323

globalKoin.createScope<UserSession>("session-${sessionId()}")

324

}

325

326

private val requestScope: Scope by lazy {

327

globalKoin.createScope<RequestContext>("request-${requestId()}")

328

}

329

330

fun getSessionService(): SessionService = sessionScope.get()

331

fun getRequestService(): RequestService = requestScope.get()

332

333

private fun sessionId(): String = "session-123"

334

private fun requestId(): String = "request-456"

335

}

336

```

337

338

### Component with Conditional Injection

339

340

```kotlin

341

class ConditionalComponent : KoinComponent {

342

private val featureFlag: FeatureFlag by inject()

343

344

// Conditional lazy injection based on feature flags

345

private val newFeatureService: NewFeatureService? by lazy {

346

if (featureFlag.isEnabled("new_feature")) {

347

get<NewFeatureService>()

348

} else {

349

null

350

}

351

}

352

353

fun performAction() {

354

if (newFeatureService != null) {

355

newFeatureService.performNewAction()

356

} else {

357

// Fallback behavior

358

performLegacyAction()

359

}

360

}

361

362

private fun performLegacyAction() {

363

// Legacy implementation

364

}

365

}

366

```

367

368

### Component Inheritance

369

370

```kotlin

371

abstract class BaseComponent : KoinComponent {

372

protected val logger: Logger by inject()

373

protected val config: Config by inject()

374

375

protected fun logOperation(operation: String) {

376

logger.info("$operation in ${this::class.simpleName}")

377

}

378

}

379

380

class UserComponent : BaseComponent() {

381

private val userService: UserService by inject()

382

383

fun processUser(userId: String) {

384

logOperation("processUser") // Uses inherited logger

385

userService.process(userId)

386

}

387

}

388

389

class OrderComponent : BaseComponent() {

390

private val orderService: OrderService by inject()

391

392

fun processOrder(orderId: String) {

393

logOperation("processOrder") // Uses inherited logger

394

orderService.process(orderId)

395

}

396

}

397

```

398

399

## Testing with Components

400

401

### Component Testing

402

403

```kotlin

404

class TestableComponent : KoinComponent {

405

private val service: MyService by inject()

406

407

fun doSomething(): String {

408

return service.getData()

409

}

410

}

411

412

// Test with mock

413

@Test

414

fun testComponent() {

415

val testModule = module {

416

single<MyService> { mockk<MyService> {

417

every { getData() } returns "test data"

418

}}

419

}

420

421

val testApp = koinApplication { modules(testModule) }

422

423

val component = TestableComponent()

424

// Component uses test Koin context automatically

425

assertEquals("test data", component.doSomething())

426

}

427

```

428

429

### Scoped Component Testing

430

431

```kotlin

432

@Test

433

fun testScopedComponent() {

434

val testModule = module {

435

scope<TestScope> {

436

scoped<ScopedService> { mockk<ScopedService>() }

437

}

438

}

439

440

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

441

442

class TestComponent : KoinScopeComponent {

443

override val scope = koin.createScope<TestScope>("test")

444

445

fun getService(): ScopedService = get()

446

}

447

448

val component = TestComponent()

449

assertNotNull(component.getService())

450

451

component.scope.close()

452

}

453

```

454

455

## Best Practices

456

457

### 1. Use Lazy Injection for Expensive Resources

458

459

```kotlin

460

class OptimizedComponent : KoinComponent {

461

// Lazy injection - only created when needed

462

private val expensiveService: ExpensiveService by inject()

463

464

// Direct injection for lightweight dependencies

465

private val config: Config = get()

466

467

fun performOperation() {

468

// ExpensiveService is created here, not at component creation

469

expensiveService.doWork()

470

}

471

}

472

```

473

474

### 2. Proper Scope Lifecycle Management

475

476

```kotlin

477

class ProperScopeComponent : KoinScopeComponent {

478

override val scope: Scope by lazy { createScope() }

479

480

fun initialize() {

481

// Setup scope dependencies

482

}

483

484

fun cleanup() {

485

// Always close scope when done

486

if (!scope.closed) {

487

scope.close()

488

}

489

}

490

}

491

```

492

493

### 3. Avoid Direct Koin Access

494

495

```kotlin

496

// Good - uses component interface

497

class GoodComponent : KoinComponent {

498

private val service: MyService by inject()

499

}

500

501

// Avoid - direct Koin dependency

502

class AvoidComponent(private val koin: Koin) {

503

private val service: MyService = koin.get()

504

}

505

```

506

507

### 4. Component Interface Segregation

508

509

```kotlin

510

// Specific interfaces for different component types

511

interface ServiceComponent : KoinComponent

512

interface RepositoryComponent : KoinComponent

513

interface ScopedServiceComponent : KoinScopeComponent

514

515

class UserService : ServiceComponent {

516

private val repository: UserRepository by inject()

517

}

518

519

class UserSessionManager : ScopedServiceComponent {

520

override val scope: Scope by lazy { createScope() }

521

private val sessionData: SessionData by inject()

522

}

523

```

524

525

Component interfaces provide a clean, testable way to integrate dependency injection into your application architecture while maintaining loose coupling and supporting various lifecycle patterns.