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
}