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
```