zio/zio1
ZLayer module pattern 을 이용한 dependency injection
wefree
2021. 8. 18. 17:44
문제
zio module pattern 1.0 은 쓸데없이 복잡해 차라리 constructor dependency injection 을 쓰는게 낫다고 생각해 왔다. zio module pattern 2.0 이 최근에 소개되었는데, zio magic 과 함께 쓰면 어느정도 쓸만한 것 같다.
코드
import zio.clock.Clock
import zio.console.Console
import zio.magic._
import zio.{ App, ExitCode, Has, Ref, UIO, URIO, URLayer, ZIO, ZLayer }
object ZLayerTest extends App {
trait Logging {
def log(line: String): UIO[Unit]
}
object Logging {
// URIO 가 아닌 ZStream 일 경우 ZIO.serviceWith 대신에
// ZStream.accessStream(_.get.XXX) 사용
def log(line: String): URIO[Has[Logging], Unit] =
ZIO.serviceWith[Logging](_.log(line))
val refLayer: ZLayer[Any, Nothing, Has[Ref[Long]]] = Ref.make(0L).toLayer
val fullLayer: URLayer[Has[Console.Service] with Has[Clock.Service] with Has[Ref[Long]], Has[Logging]] =
LoggingLive.toLayer[Logging]
val live: ZLayer[Has[Console.Service] with Has[Clock.Service], Nothing, Has[Logging]] =
ZLayer.wireSome[Has[Console.Service] with Has[Clock.Service], Has[Logging]](refLayer, fullLayer)
}
case class LoggingLive(console: Console.Service, clock: Clock.Service, ref: Ref[Long]) extends Logging {
override def log(line: String): UIO[Unit] =
for {
current <- clock.currentDateTime.orDie
count <- ref.modify(x => (x + 1, x + 1))
_ <- console.putStrLn(current.toString + s": [$count] " + line).orDie
} yield ()
}
override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = {
val program: URIO[Has[Logging], Unit] = Logging.log("Hi") *> Logging.log("Hello")
program.injectSome[zio.ZEnv](Logging.live).exitCode
}
}
출력 결과 샘플
2021-08-19T01:10:28.234+09:00: [1] Hi
2021-08-19T01:10:28.257+09:00: [2] Hello