Dependency injection with Scaldi

2016-02-23

Scaldi is a lightweight dependency injection framework for Scala. In Play, it provides a more Scala-like approach to injecting dependencies than the out of the box solution based on Google Guice.

Installation

My examples are based on a so-called starting-point. The starting-point is basically a slightly simplified Activator template app. If you want to follow, just clone the book's repo and create a new branch:

$ git clone https://github.com/denisftw/modern-web-scala .
$ git checkout tags/starting-point -b blog-branch

The official Scaldi website suggests we add two libraries - the library itself and the Play integration part:

libraryDependencies ++= Seq(
  // ...
  "org.scaldi" %% "scaldi" % "0.5.7",
  "org.scaldi" %% "scaldi-play" % "0.5.13"
)

After that we should inform Play that we're going to use this library and not the default one in application.conf:

play.application.loader = scaldi.play.ScaldiApplicationLoader
play.modules.enabled += modules.MainModule

Here we're specifying the ScaldiApplicationLoader as the application loader and adding a module that will contain our services.

Defining modules

We specified our module in the configuration file, but it doesn't exist. Let's create a file called MainModule in package modules:

class MainModule extends Module {
  binding to new Application
}

And that's it. After reloading, Play will download necessary dependencies from the Internet and Scaldi will start managing the app.

Managing the application lifecycle

In early days of Play, there was a trait called GlobalSettings, which you would use for registering lifecycle callbacks:

object Global extends GlobalSettings {
    override def onStart(app: Application) {
        super.onStart(app)
    }
    override def onStop(app: Application) {
        super.onStop(app)
    }
}

In Play 2.4, this approach is deprecated and we need to use something else. Fortunately, Scaldi offers so-called eagerly initialized bindings:

class MainModule extends Module {
  // ...
  binding toNonLazy new Initializer initWith(_.init()) destroyWith(_.stop())
}

And the good thing about it is that Initializer could be a simple class and doesn't even have to extend any traits:

class Initializer {
  def init() = {
    Logger.info("Starting...")
  }
  def stop() = {
    Logger.info("Stopping...")
  }
}

Here we're simply printing log messages, but it's certainly possible to do something more useful.

Injecting services

The most common way to initialize a new service is to use lazy binding. Let's add a new service in the MainModule trait:

class MainModule extends Module {
  // ...
  binding to new GreetingService
}

The service itself is just a class. If we're not going to inject other services into GreetingService itself, it could be very simple:

class GreetingService {
  def greet(name: String): String = s"Hello $name"
}

After the GreetingService is defined inside the module, we can inject it into the Application controller:

class Application(implicit inj: Injector) extends Controller with Injectable {

  val greetingService = inject[GreetingService]

  def greet(name: String) = Action {
    val greeting = greetingService.greet(name)
    Ok(Json.toJson(greeting))
  }

  // ...
}

The trick here is to add an implicit Injector parameter to the Application controller. Extending the Injectable trait makes the inject method available. In my opinion, this approach looks much nicer than what Guice offers.

There are many more features that are available in Scaldi. Definitely check it out if you need a runtime DI and/or you don't feel like using Guice annotations in Scala code.

Concusion

This is the first post in a series of posts about the Scala ecosystem. Next time, I'm going to integrate ScalikeJDBC into this sample application. Also, if you want to get familiar with the Scala ecosystem while learning the language, take a look at my book Modern Web Development with Scala.