Skip to main content
Version: 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
}