-
-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathZLayerIntegrationExample.scala
More file actions
124 lines (104 loc) · 4.48 KB
/
ZLayerIntegrationExample.scala
File metadata and controls
124 lines (104 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package nl.vroste.rezilience.examples
import nl.vroste.rezilience.Policy.PolicyError
import nl.vroste.rezilience.{ Bulkhead, CircuitBreaker, RateLimiter }
import zio._
/**
* Example of how to integrate a rate limiter to an entire Service using ZLayer
*/
object ZLayerIntegrationExample extends zio.ZIOAppDefault {
type Account = String
type Amount = Int
// The definition of our Database service
type Database = Database.Service
type ResilientDatabase = ResilientDatabase.Service
object Database {
trait Service {
def transfer(amount: Amount, from: Account, to: Account): ZIO[Any, Throwable, Unit]
def newAccount(name: Account): ZIO[Any, Throwable, Unit]
}
// Accessors
def transfer(amount: Amount, from: Account, to: Account): ZIO[Database, Throwable, Unit] =
ZIO.service[Database.Service].flatMap(_.transfer(amount, from, to))
def newAccount(name: Account): ZIO[Database, Throwable, Unit] =
ZIO.service[Database.Service].flatMap(_.newAccount(name))
}
object ResilientDatabase {
trait Service {
def transfer(amount: Amount, from: Account, to: Account): ZIO[Any, PolicyError[Throwable], Unit]
def newAccount(name: Account): ZIO[Any, PolicyError[Throwable], Unit]
}
// Accessors
def transfer(amount: Amount, from: Account, to: Account): ZIO[ResilientDatabase, PolicyError[Throwable], Unit] =
ZIO.service[ResilientDatabase.Service].flatMap(_.transfer(amount, from, to))
def newAccount(name: Account): ZIO[ResilientDatabase, PolicyError[Throwable], Unit] =
ZIO.service[ResilientDatabase.Service].flatMap(_.newAccount(name))
}
// Some live implementation of our Database service
val databaseLayer: ULayer[Database] = ZLayer.succeed {
new Database.Service {
override def transfer(amount: Amount, from: Account, to: Account): ZIO[Any, Throwable, Unit] =
ZIO.succeed(println("transfer"))
override def newAccount(name: Account): ZIO[Any, Throwable, Unit] =
ZIO.succeed(println("new account"))
}
}
// A layer that adds a rate limiter to our database
val addRateLimiterToDatabase: ZLayer[Database, Nothing, Database] =
ZLayer.scoped {
ZIO
.service[Database.Service]
.flatMap { (database: Database.Service) =>
RateLimiter.make(10).map { rl =>
new Database.Service {
override def transfer(amount: Amount, from: Account, to: Account): ZIO[Any, Throwable, Unit] =
rl(database.transfer(amount, from, to))
override def newAccount(name: Account): ZIO[Any, Throwable, Unit] =
rl(database.newAccount(name))
}
}
}
}
val addBulkheadToDatabase: ZLayer[Database, Nothing, Database] =
ZLayer.scoped {
ZIO
.service[Database.Service]
.flatMap { (database: Database.Service) =>
Bulkhead
.make(10)
.map { bulkhead =>
new Database.Service {
override def transfer(amount: Amount, from: Account, to: Account): ZIO[Any, Throwable, Unit] =
bulkhead(database.transfer(amount, from, to)).mapError(_.toException)
override def newAccount(name: Account): ZIO[Any, Throwable, Unit] =
bulkhead(database.newAccount(name)).mapError(_.toException)
}
}
}
}
val addCircuitBreakerToDatabase: ZLayer[Database, Nothing, ResilientDatabase] =
ZLayer.scoped {
ZIO
.service[Database.Service]
.flatMap { (database: Database.Service) =>
CircuitBreaker
.withMaxFailures[Throwable](10)
.map(_.toPolicy)
.map { rl =>
new ResilientDatabase.Service {
override def transfer(amount: Amount, from: Account, to: Account)
: ZIO[Any, PolicyError[Throwable], Unit] =
rl(database.transfer(amount, from, to))
override def newAccount(name: Account): ZIO[Any, PolicyError[Throwable], Unit] =
rl(database.newAccount(name))
}
}
}
}
// The complete environment of our application
val env: ZLayer[Any, Nothing, ResilientDatabase] =
databaseLayer >+> addRateLimiterToDatabase >>> addCircuitBreakerToDatabase
// Run our program against the Database service being unconcerned with the rate limiter
override def run =
(ResilientDatabase.transfer(1, "a", "b") *> ResilientDatabase.transfer(3, "b", "a"))
.provideLayer(env)
}