0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ZIOのDIについて

Last updated at Posted at 2023-09-28

DIについて

DIについての説明とメリットについては以下のページに記載がありますが、先の章でお伝えした通り、このDIの仕組みがZIOには元々備わっています。

DIを導入することでテスト時に単体テスト用のモックオブジェクト(ダミーオブジェクト)に切り替えることなどができ、単体テストを行いやすくなります。

サービスパターン

ZIOのDIの使用の流れを実際に知るため、クリーンアーキテクチャとも関係が深いオニオンアーキテクチャにも適用可能な公式が提唱しているサービスパターンを先の章のHello, World!プログラムに導入します。

  • オニオンアーキテクチャの概略図は以下になります。
    オニオンアーキテクチャ.jpg

    オニオンアーキテクチャでは階層構造を円で表現する。オニオン(=玉ねぎ)という名称は、おそらくこの円構造からきているものと思われる。
    全ての依存関係は円の中心の層に対して向かう。一方で、中心の層から外側の層へは依存しない。アプリケーションのコア(核)になる層の数は変化しても良いが、ドメインモデル層は常に中心に配置する。

    出典:オニオンアーキテクチャの階層構造

ZIOのサービスパターンは関数型プログラミングとオブジェクト指向プログラミング両方の利点が得られるパターンであるとされています。
2023/07/25 Hello, World!というように日付も出力されるように修正します。
Date型をDIの対象とします。

サービスを定義(Service Definition)

最初に大元となるconsoleOutputといった文字列出力関数の定義をApplicationServiceという名前のtraitに追加します。

ApplicationService.scala
trait ApplicationService {
  def consoleOutput(): ZIO[Any, Throwable, Unit]
}

サービスの実装(Service Implementation)

「サービスを定義」で作成したtraitの実装としてApplicationServiceImplという名前のcase classを作成します。

ApplicationServiceImpl.scala
case class ApplicationServiceImpl(currentDate: Date) extends ApplicationService {
  override def consoleOutput(): ZIO[Any, Throwable, Unit] = ???
}

サービスの依存性(Service Dependencies)

サービスの依存性について注入できるようにコンストラクタを作っています。
本パターンではコンストラクタインジェクションにより依存性を注入します。
それと同時に具体的な実装を行っています。

ApplicationServiceImpl.scala
case class ApplicationServiceImpl(currentDate: Date) extends ApplicationService {
  override def consoleOutput(): ZIO[Any, Throwable, Unit] =
    Console.printLine(
      s"${new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(currentDate)} Hello, World!"
    )
}

ZLayer(ZLayer (Constructor))

依存性を注入されたApplicationServiceを返すlayerを定義します。
ZIOでは一般的には依存性はZLayerでラップし、他のサービスなどへの依存性注入に使います。
そのため、ここでも返り値はZLayer型となっています。

ApplicationServiceImpl.scala
object ApplicationServiceImpl {
  val layer: ZLayer[Date, Nothing, ApplicationService] =
    ZLayer {
      for {
        currentDate <- ZIO.service[Date]
      } yield ApplicationServiceImpl(currentDate)
    }
}

アクセサメソッド(Accessor Methods)

単純に例えばApplicationService.consoleOutputといったように呼び出せるようにコンパニオンオブジェクトを使いアクセサメソッドを作成します。

ApplicationService.scala
object ApplicationService {
  def consoleOutput(): ZIO[ApplicationService, Throwable, Unit] =
    ZIO.serviceWithZIO[ApplicationService](_.consoleOutput())
}

依存性の注入と関数の使用

アプリケーションの本体となるサービスが完成したので、実際に使っていきます。
ApplicationService.consoleOutputの返り値はZIO[ApplicationService, Throwable, Unit]となっていることからこの関数はApplicationService型に依存していることがわかります。
ApplicationService型については、その実装型であるApplicationServiceImpl.layerを注入することで解決できます。
しかしながら、ApplicationServiceImpl.layerの返り値がZLayer[Date, Nothing, ApplicationService]であることから、さらにApplicationServiceImpl.layerDate型に依存していることがわかります(Zlayer型の解釈についてはZIO型とほぼ同等であると考えてください)。
以上からApplicationService.consoleOutputを使うためには最終的に以下の2つを依存性として注入する必要があることがわかりました。

  1. ApplicationServiceImpl.layer
  2. Date

run関数からのApplicationService.consoleOutputの呼び出しは以下のようになります。

main.scala
  def run: ZIO[Any, Throwable, Unit] = ApplicationService.consoleOutput().provide(ZLayer.fromZIO(ZIO.attempt {
    import java.text.SimpleDateFormat
    // 任意の日付文字列
    val inpDateStr = "2023/07/25 17:46:00"

    // 取り扱う日付の形にフォーマット設定
    val sdformat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")

    // Date型に変換( DateFromatクラスのparse() )
    sdformat.parse(inpDateStr)
  }), ApplicationServiceImpl.layer)

provide関数を使い、依存性を注入しています。
このprovide関数は引数の順序に依らず、型に応じて自動的に依存性を解決してくれる関数です。
また、ここではZLayer.fromZIOを使い、日時を自由に指定できるようにDate型を作成しています。

終わりに

新しい関数や型が多く登場しましたが、まとめるとZIOの基本的なDIの使い方は以下の流れになります。

  1. サービスパターンにより依存性を持つ関数を作成する(他に依存しない関数の作成も可能です)。
  2. provide関数などで依存性を解決した上でrun関数に渡し、実行する。

また、上記の通りZIOの依存性の解決についてはにより決定されるという特徴があることも分かったかと思います。

次に次章ではZIO型に関係するZIOの例外処理について詳しく見ていきます。

前章:Hello, World!
次章:ZIOのエラー処理

演習

  1. 今回のサービスパターン、DIパターンを導入したHello, World!プログラムを実際に動かしてください。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?