ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ZLayer 를 이용한 dependency injection
    zio/zio2 2022. 7. 27. 20:05

    문제

    zio1 에서의 zlayer example 을 zio2 의 zlayer 를 이용해 구현해 보자

     

    코드

    아래처럼는 공식 가이드를 이용해 작성한 코드이지만.. 개인적으로 생각하는 코드는 바로 아래에 추가했다.

    import zio._
    
    import java.util.concurrent.TimeUnit
    
    object ZLayerTest extends zio.ZIOAppDefault {
      trait Logging {
        def log(line: String): UIO[Unit]
      }
    
      object Logging {
        def log(line: String): URIO[Logging, Unit] =
          ZIO.serviceWithZIO[Logging](_.log(line))
      }
    
      case class LoggingLive(ref: Ref[Long]) extends Logging {
        override def log(line: String): UIO[Unit] =
          for {
            current <- Clock.currentDateTime
            count   <- ref.modify(x => (x + 1, x + 1))
            _       <- Console.printLine(current.toString + s"\t[$count]\t" + line).orDie
          } yield ()
      }
    
      object LoggingLive {
        val layer: ZLayer[Ref[Long], Nothing, LoggingLive] = ZLayer.fromFunction(LoggingLive.apply _)
    
        //    val layer: ZLayer[Ref[Long], Nothing, LoggingLive] =
        //      ZLayer {
        //        for {
        //          ref <- ZIO.service[Ref[Long]]
        //        } yield LoggingLive(ref)
        //      }
      }
    
      // passthrough 를 사용하면 R 부분을 그대로 유지하면서 A 쪽으로 정보를 그대로 넘길 수 있다.
    //  val layerPassthrough: ZLayer[Ref[Long], Nothing, Ref[Long] with LoggingLive] = LoggingLive.layer.passthrough
    
      // launch = creates a ZIO that uses the services and never finishes
    //  val layerLaunch: ZIO[Ref[Long], Nothing, Nothing] = LoggingLive.layer.launch
    
      // fresch
    //  val layerFresh = LoggingLive.layer.fresh
    
      val program: ZIO[Logging, Nothing, Unit] = Logging.log("Hi") *> Logging.log("Hello")
    
      val refLayer: ZLayer[Any, Nothing, Ref[Long]] = ZLayer(Ref.make(0L))
    
      
    //  val loggingZIO: ZIO[Any, Nothing, Logging] = LoggingLive.layer.provideSomeLayer(refLayer)
    //  아래처럼 ZLayer.make() 를 이용해 따로 만들 수도 있다.
    //  val loggingLiveLayer: ZLayer[Any, Nothing, Logging] = ZLayer.make[Logging](LoggingLive.layer, refLayer)
    //  def run = program.provide(loggingLiveLayer)
    
      // 개발할 때 ZLayer.Debug.mermaid 또는 ZLayer.Debug.tree 를 추가하면 tree 형태로 dependency 를 보여줌
      def run = program.provide(LoggingLive.layer, refLayer, ZLayer.Debug.mermaid)
    
      /*
         Already provides services: Clock, Random, System, Console
       */
    //  val getTime = Clock.currentTime(TimeUnit.SECONDS)
    //  val randomValue = Random.nextInt
    //  val sysVariable = System.env("HADOOP_HOME")
    }

     

    공식 가이드는 위와 같이 case class 를 활용을 권장하지만, zlayer 로 변환 그리고 ZIO 의 R 과 결합되면서 매우 복잡해진다. 개인적으로는 아래처럼 코드를 작성하는 것이 더 심플하다고 생각한다. program 전반에 걸쳐 dependency 는 ZIO 의 R 로 관리하고, 마지막 결합에서 ZIO 를 Zlayer 로 변환해 provide 한다.

     

    import zio._
    
    object ZioApp extends zio.ZIOAppDefault {
      trait Logging {
        def log(line: String): UIO[Unit]
      }
    
      object Logging {
        def log(line: String): URIO[Logging, Unit] = ZIO.serviceWithZIO(_.log(line))
      }
    
      val someLoggingZIO: ZIO[Ref[Long], Nothing, Logging] = for {
        ref <- ZIO.service[Ref[Long]]
      } yield new Logging {
        override def log(line: String): UIO[Unit] =
          for {
            current <- Clock.currentDateTime
            count <- ref.modify(x => (x + 1, x + 1))
            _ <- Console
              .printLine(current.toString + s"\t[$count]\t" + line)
              .orDie
          } yield ()
      }
    
    //  val someLoggingZIO: ZIO[Ref[Long], Nothing, Logging] =
    //    ZIO.serviceWith[Ref[Long]] { ref =>
    //      new Logging {
    //        override def log(line: String): UIO[Unit] =
    //          for {
    //            current <- Clock.currentDateTime
    //            count <- ref.modify(x => (x + 1, x + 1))
    //            _ <- Console
    //              .printLine(current.toString + s"\t[$count]\t" + line)
    //              .orDie
    //          } yield ()
    //      }
    //    }
    
      val program: ZIO[Logging, Nothing, Unit] =
        Logging.log("Hi") *> Logging.log("Hello")
    
      override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = {
        val refLive: UIO[Ref[Long]] = Ref.make(0L)
        val loggingLive: ZIO[Ref[Long], Nothing, Logging] = someLoggingZIO
    
        val refLayer = ZLayer(refLive)
        val loggingLayer = ZLayer(loggingLive)
    
        program.provide(loggingLayer, refLayer, ZLayer.Debug.mermaid)
      }
    }

     

    Scope 활용

    Scope 를 이용한 resource 관리가 필요할 경우 ZLayer.scoped 를 사용한다.

    import zio._
    
    import java.io.IOException
    import scala.io._
    
    object ZLayerTest extends zio.ZIOAppDefault {
      trait Show {
        def print(line: String): Task[Unit]
      }
    
      object Show {
        def print(line: String): ZIO[Show, Throwable, Unit] =
          ZIO.serviceWithZIO(_.print(line))
      }
    
      case class ShowLive(source: Source) extends Show {
        override def print(line: String): Task[Unit] =
          Console.printLine(source.getLines().toList.headOption.getOrElse("") + "\n" + line)
      }
    
      object ShowLive {
        val layer: ZLayer[Source, Nothing, ShowLive] = ZLayer.fromFunction(ShowLive.apply _)
      }
    
      val source: ZIO[Scope, IOException, Source] = {
        def acquire(name: => String): ZIO[Any, IOException, Source] = 
          ZIO.attemptBlockingIO(Source.fromFile(name))
        def release(source: => Source): ZIO[Any, Nothing, Unit] = 
          ZIO.succeedBlocking(source.close())
          
        ZIO.acquireRelease(acquire("app.conf"))(release(_))
      }
    
      val program: ZIO[Show, Throwable, Unit] = Show.print("Show me the money")
    
      // val sourceZIO: ZIO[Any, IOException, Source] = ZIO.scoped(source)
      val sourceLayer: ZLayer[Any, IOException, Source] = ZLayer.scoped { source }
    
      def run = program.provide(ShowLive.layer, sourceLayer)
    }

    'zio > zio2' 카테고리의 다른 글

    Ref vs Ref.Synchronized  (0) 2022.12.09
    Fiber  (0) 2022.11.23
    Error Models  (0) 2022.11.20
    Option 처리하기  (0) 2022.11.20
    java callback 을 ZIO 로 바꾸기  (0) 2022.07.17

    댓글

Designed by Tistory.