or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application.mdconcurrency.mdcore-effects.mddependency-injection.mderror-handling.mdindex.mdmetrics.mdresource-management.mdservices.mdstm.mdstreams.mdtesting.md

dependency-injection.mddocs/

0

# Dependency Injection

1

2

ZLayer provides a powerful dependency injection system for building and composing services with compile-time dependency resolution and automatic resource management.

3

4

## Capabilities

5

6

### ZLayer Type

7

8

The core layer type for describing how to build services with dependency injection.

9

10

```scala { .api }

11

/**

12

* A ZLayer describes how to build services of type ROut from dependencies of type RIn

13

* - RIn: Required dependencies

14

* - E: Error type during construction

15

* - ROut: Services provided by this layer

16

*/

17

sealed abstract class ZLayer[-RIn, +E, +ROut]

18

19

// Layer type aliases

20

type RLayer[-RIn, +ROut] = ZLayer[RIn, Throwable, ROut]

21

type URLayer[-RIn, +ROut] = ZLayer[RIn, Nothing, ROut]

22

type Layer[+E, +ROut] = ZLayer[Any, E, ROut]

23

type ULayer[+ROut] = ZLayer[Any, Nothing, ROut]

24

type TaskLayer[+ROut] = ZLayer[Any, Throwable, ROut]

25

```

26

27

### Layer Construction

28

29

Create layers from values, effects, and scoped resources.

30

31

```scala { .api }

32

/**

33

* Create a layer from a constant value

34

*/

35

def succeed[A: Tag](a: => A): ULayer[A]

36

37

/**

38

* Create a layer that always fails

39

*/

40

def fail[E](e: => E): Layer[E, Nothing]

41

42

/**

43

* Create a layer from a ZIO effect

44

*/

45

def fromZIO[R, E, A: Tag](zio: => ZIO[R, E, A]): ZLayer[R, E, A]

46

47

/**

48

* Create a layer from another layer by passing through dependencies

49

*/

50

def service[A: Tag]: ZLayer[A, Nothing, A]

51

52

/**

53

* Create a layer from a scoped resource (with automatic cleanup)

54

*/

55

def scoped[R]: ScopedPartiallyApplied[R]

56

57

/**

58

* Create a layer from a function of dependencies

59

*/

60

def fromFunction[In]: FunctionConstructor[In]#Out

61

62

/**

63

* Create multiple instances of a service in parallel

64

*/

65

def collectAll[R, E, A: Tag](layers: Iterable[ZLayer[R, E, A]]): ZLayer[R, E, List[A]]

66

67

/**

68

* Create a layer that provides debug information

69

*/

70

def debug: ULayer[Unit]

71

```

72

73

**Usage Examples:**

74

75

```scala

76

import zio._

77

78

// From constant value

79

val configLayer = ZLayer.succeed(AppConfig(port = 8080, debug = true))

80

81

// From effect

82

val databaseLayer = ZLayer.fromZIO {

83

ZIO.attempt(new DatabaseImpl("jdbc:postgresql://localhost/mydb"))

84

}

85

86

// From scoped resource (auto-cleanup)

87

val connectionLayer = ZLayer.scoped {

88

ZIO.acquireRelease(

89

ZIO.attempt(Connection.create())

90

)(conn => ZIO.succeed(conn.close()).ignore)

91

}

92

93

// From function of dependencies

94

val userServiceLayer = ZLayer.fromFunction(UserServiceImpl.apply _)

95

96

// Pass-through layer

97

val configPassthrough = ZLayer.service[AppConfig]

98

```

99

100

### Layer Composition

101

102

Compose layers using various operators for building complex dependency graphs.

103

104

```scala { .api }

105

/**

106

* Combine two layers horizontally (both outputs available)

107

*/

108

def ++[E1 >: E, RIn2, ROut2](that: => ZLayer[RIn2, E1, ROut2]): ZLayer[RIn with RIn2, E1, ROut with ROut2]

109

110

/**

111

* Compose layers vertically (output of first feeds into second)

112

*/

113

def >>>[E1 >: E, ROut2](that: => ZLayer[ROut, E1, ROut2]): ZLayer[RIn, E1, ROut2]

114

115

/**

116

* Compose and merge outputs (both outputs available after composition)

117

*/

118

def >+>[E1 >: E, ROut2](that: => ZLayer[ROut, E1, ROut2]): ZLayer[RIn, E1, ROut with ROut2]

119

120

/**

121

* Use this layer only if condition is true

122

*/

123

def when[R1 <: RIn](p: => Boolean): ZLayer[R1, E, Option[ROut]]

124

125

/**

126

* Provide partial environment to this layer

127

*/

128

def provideSomeEnvironment[RIn0](f: ZEnvironment[RIn0] => ZEnvironment[RIn]): ZLayer[RIn0, E, ROut]

129

130

/**

131

* Fresh instance for each use (no sharing)

132

*/

133

def fresh: ZLayer[RIn, E, ROut]

134

135

/**

136

* Memoized layer (singleton instance)

137

*/

138

def memoize: UIO[ZLayer[RIn, E, ROut]]

139

```

140

141

**Usage Examples:**

142

143

```scala

144

// Horizontal composition (combine outputs)

145

val dataLayer = databaseLayer ++ cacheLayer ++ loggerLayer

146

147

// Vertical composition (chain dependencies)

148

val fullStack = configLayer >>> databaseLayer >>> userServiceLayer

149

150

// Compose and merge

151

val enrichedService = databaseLayer >+> (auditLayer >>> userServiceLayer)

152

153

// Complex dependency graph

154

val appLayer =

155

configLayer ++

156

loggerLayer ++

157

(configLayer >>> databaseLayer) ++

158

(configLayer ++ databaseLayer >>> userServiceLayer)

159

```

160

161

### Layer Transformation

162

163

Transform layer inputs, outputs, and errors.

164

165

```scala { .api }

166

/**

167

* Transform the output environment

168

*/

169

def map[ROut1](f: ZEnvironment[ROut] => ZEnvironment[ROut1]): ZLayer[RIn, E, ROut1]

170

171

/**

172

* Transform the error type

173

*/

174

def mapError[E1](f: E => E1): ZLayer[RIn, E1, ROut]

175

176

/**

177

* Transform both error and output

178

*/

179

def bimap[E1, ROut1](f: E => E1, g: ZEnvironment[ROut] => ZEnvironment[ROut1]): ZLayer[RIn, E1, ROut1]

180

181

/**

182

* Recover from all layer construction errors

183

*/

184

def catchAll[RIn1 <: RIn, E1, ROut1 >: ROut](handler: E => ZLayer[RIn1, E1, ROut1]): ZLayer[RIn1, E1, ROut1]

185

186

/**

187

* Provide a fallback layer if construction fails

188

*/

189

def orElse[RIn1 <: RIn, E1, ROut1 >: ROut](that: => ZLayer[RIn1, E1, ROut1]): ZLayer[RIn1, E1, ROut1]

190

191

/**

192

* Retry layer construction according to a schedule

193

*/

194

def retry[RIn1 <: RIn, S](schedule: => Schedule[RIn1, E, S]): ZLayer[RIn1, E, ROut]

195

196

/**

197

* Project out a specific service from the layer output

198

*/

199

def project[A: Tag](implicit ev: A <:< ROut): ZLayer[RIn, E, A]

200

```

201

202

**Usage Examples:**

203

204

```scala

205

// Transform output

206

val enhancedDb = databaseLayer.map { env =>

207

val db = env.get[Database]

208

ZEnvironment(DatabaseWithMetrics(db))

209

}

210

211

// Error recovery

212

val resilientLayer = databaseLayer.catchAll { error =>

213

Console.printLineError(s"DB failed: $error") *>

214

ZLayer.succeed(MockDatabase())

215

}

216

217

// Fallback chain

218

val reliableDb =

219

primaryDatabaseLayer

220

.orElse(secondaryDatabaseLayer)

221

.orElse(ZLayer.succeed(LocalDatabase()))

222

```

223

224

### Layer Lifecycle

225

226

Build, launch, and manage layer lifecycles with proper resource management.

227

228

```scala { .api }

229

/**

230

* Build the layer into a scoped ZIO effect

231

*/

232

def build: ZIO[RIn with Scope, E, ZEnvironment[ROut]]

233

234

/**

235

* Launch the layer and run it forever (for daemon services)

236

*/

237

def launch: ZIO[RIn, E, Nothing]

238

239

/**

240

* Convert layer to a Runtime with the provided services

241

*/

242

def toRuntime: ZIO[Scope, E, Runtime[ROut]]

243

244

/**

245

* Provide this layer to a ZIO effect

246

*/

247

def apply[R1 <: ROut, E1 >: E, A](zio: ZIO[R1, E1, A]): ZIO[RIn, E1, A]

248

```

249

250

**Usage Examples:**

251

252

```scala

253

// Build layer in scope

254

val program = ZIO.scoped {

255

for {

256

env <- appLayer.build

257

_ <- program.provideEnvironment(env)

258

} yield ()

259

}

260

261

// Launch daemon service

262

val server = httpServerLayer.launch

263

264

// Use layer with effect

265

val result = appLayer {

266

for {

267

user <- ZIO.service[UserService]

268

_ <- user.createUser("Alice")

269

} yield ()

270

}

271

```

272

273

### Environment and Service Access

274

275

Work with ZEnvironment and service tags for type-safe dependency access.

276

277

```scala { .api }

278

/**

279

* Tag for service identification in dependency injection

280

*/

281

sealed trait Tag[A] extends EnvironmentTag[A] {

282

def tag: LightTypeTag

283

}

284

285

/**

286

* Type-safe environment container

287

*/

288

sealed trait ZEnvironment[+R] {

289

def get[A >: R](implicit tag: Tag[A]): A

290

def add[A: Tag](a: A): ZEnvironment[R with A]

291

def union[R1](that: ZEnvironment[R1]): ZEnvironment[R with R1]

292

def prune[R1](implicit tag: EnvironmentTag[R1]): ZEnvironment[R1]

293

}

294

295

object ZEnvironment {

296

def empty: ZEnvironment[Any]

297

def apply[A: Tag](a: A): ZEnvironment[A]

298

}

299

```

300

301

**Usage Examples:**

302

303

```scala

304

// Create environment

305

val env = ZEnvironment(DatabaseImpl()) ++ ZEnvironment(CacheImpl())

306

307

// Access services

308

val database = env.get[Database]

309

val cache = env.get[Cache]

310

311

// Service-based layer construction

312

case class UserService(db: Database, cache: Cache)

313

314

val userServiceLayer = ZLayer {

315

for {

316

db <- ZIO.service[Database]

317

cache <- ZIO.service[Cache]

318

} yield UserService(db, cache)

319

}

320

```

321

322

### Layer Patterns

323

324

Common patterns for organizing and structuring layer dependencies.

325

326

```scala { .api }

327

// Service pattern - single responsibility

328

trait UserRepository {

329

def findUser(id: Long): Task[Option[User]]

330

def saveUser(user: User): Task[Unit]

331

}

332

333

val userRepositoryLayer = ZLayer {

334

for {

335

db <- ZIO.service[Database]

336

} yield UserRepositoryImpl(db)

337

}

338

339

// Configuration pattern

340

case class DatabaseConfig(url: String, maxConnections: Int)

341

342

val databaseConfigLayer = ZLayer.fromZIO {

343

ZIO.config[DatabaseConfig](DatabaseConfig.config)

344

}

345

346

// Resource pattern with cleanup

347

val connectionPoolLayer = ZLayer.scoped {

348

ZIO.acquireRelease(

349

ZIO.attempt(ConnectionPool.create(maxSize = 10))

350

)(pool => ZIO.succeed(pool.shutdown()).ignore)

351

}

352

353

// Factory pattern

354

trait UserServiceFactory {

355

def create(config: UserConfig): UserService

356

}

357

358

val userServiceFactoryLayer = ZLayer.succeed {

359

new UserServiceFactory {

360

def create(config: UserConfig): UserService =

361

UserServiceImpl(config)

362

}

363

}

364

```

365

366

**Usage Examples:**

367

368

```scala

369

// Application wiring

370

val liveAppLayer =

371

// Configuration

372

configLayer ++

373

loggerLayer ++

374

375

// Infrastructure

376

(configLayer >>> databaseLayer) ++

377

(configLayer >>> cacheLayer) ++

378

379

// Services

380

(databaseLayer >>> userRepositoryLayer) ++

381

(userRepositoryLayer ++ cacheLayer >>> userServiceLayer) ++

382

383

// HTTP

384

(userServiceLayer >>> httpRoutesLayer) ++

385

(configLayer ++ httpRoutesLayer >>> httpServerLayer)

386

387

// Test layer with mocks

388

val testAppLayer =

389

ZLayer.succeed(TestConfig()) ++

390

ZLayer.succeed(MockDatabase()) ++

391

ZLayer.succeed(MockCache()) ++

392

(ZLayer.service[MockDatabase] >>> userRepositoryLayer)

393

394

// Main application

395

object MyApp extends ZIOAppDefault {

396

def run = program.provide(liveAppLayer)

397

398

val program = for {

399

server <- ZIO.service[HttpServer]

400

_ <- server.start

401

_ <- Console.printLine("Server started on port 8080")

402

_ <- ZIO.never

403

} yield ()

404

}

405

```