Skip to main content
Version: ZIO 2.x

Distage

Distage is a compile-time safe, transparent, and debuggable Dependency Injection framework for pure FP Scala.

Introduction

By using Distage we can auto-wire all components of our application.

  • We don't need to manually link components together
  • We don't need to manually specify the order of allocation and allocation of dependencies. This will be derived automatically from the dependency order.
  • We can override any component within the dependency graph.
  • It helps us to create different configurations of our components for different use cases.

Installation

In order to use this library, we need to add the following line in our build.sbt file:

libraryDependencies += "io.7mind.izumi" %% "distage-core" % "1.0.8"

Example

In this example we create a RandomApp comprising two Random and Logger services. By using ModuleDef we bind services to their implementations:

import distage.{Activation, Injector, ModuleDef, Roots}
import izumi.distage.model.Locator
import izumi.distage.model.definition.Lifecycle
import zio.{ExitCode, Task, UIO, URIO, ZIO}

import java.time.LocalDateTime

trait Random {
def nextInteger: UIO[Int]
}

final class ScalaRandom extends Random {
override def nextInteger: UIO[Int] =
ZIO.effectTotal(scala.util.Random.nextInt())
}

trait Logger {
def log(name: String): Task[Unit]
}

final class ConsoleLogger extends Logger {
override def log(line: String): Task[Unit] = {
val timeStamp = LocalDateTime.now()
ZIO.effect(println(s"$timeStamp: $line"))
}
}

final class RandomApp(random: Random, logger: Logger) {
def run: Task[Unit] = for {
random <- random.nextInteger
_ <- logger.log(s"random number generated: $random")
} yield ()
}

object DistageExample extends zio.App {
def RandomAppModule: ModuleDef = new ModuleDef {
make[Random].from[ScalaRandom]
make[Logger].from[ConsoleLogger]
make[RandomApp] // `.from` is not required for concrete classes
}

val resource: Lifecycle[Task, Locator] = Injector[Task]().produce(
plan = Injector[Task]().plan(
bindings = RandomAppModule,
activation = Activation.empty,
roots = Roots.target[RandomApp]
)
)

val myApp: Task[Unit] = resource.use(locator => locator.get[RandomApp].run)

override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] =
myApp.exitCode
}