Real-World Scala: Dependency Injection (DI)
Posted by: Jonas Boner on
<p>In this second post in the <a href="http://jonasboner.com/2008/10/01/real-world-scala-introduction/">Real-World Scala series</a> I am going to discuss how to implement/achieve <a href="http://www.martinfowler.com/articles/injection.html">Depenency Injection</a> (DI) in Scala. Scala is a very rich and deep language that gives you several ways of doing DI solely based on language constructs, but nothing prevents you from using existing Java DI frameworks, if that is preferred.</p>
<h3>Using the Cake Pattern</h3>
<p>The current strategy we are using is based on the so-called Cake Pattern. This pattern is first explained in Martin Oderskys’ paper <a href="http://lamp.epfl.ch/~odersky/papers/ScalableComponent.pdf">Scalable Component Abstractions</a> (which is an excellent paper that is highly recommended) as the way he and his team structured the Scala compiler. But rather than trying to explain the pattern and how it can be used to implement DI in plain English let’s take a look at some (naive) sample code (loosely based on our production code).</p>
<blockquote>
<p>Note: <br />
I will try to explain things in steps which I refactor towards the final version (this is only to help with the understanding), so please wait with yelling: <em>“This sucks!”</em>, until you have read and understood the final version (after which you are of course allowed come with any criticism/praise/suggestions/ideas you feel is necessary). Also, the sample code will, as in all these kind of examples, look like an insanely complicated way of doing almost nothing, but bare with me and try to envision real services in a large production system and how it applies there.</p>
</blockquote>
<p>First, let’s create a <code>UserRepository</code> (<span class="caps">DAO</span>) implementation.</p>
<pre name="code" class="scala">
// a dummy service that is not persisting anything
// solely prints out info
class UserRepository {
def authenticate(user: User): User = {
println("authenticating user: " + user)
user
}
def create(user: User) = println("creating user: " + user)
def delete(user: User) = println("deleting user: " + user)
}
</pre>
<p>Here we could have split up the implementation in a trait interface and its implementation, but in order to keep things simple I didn’t see the need.</p>
<p>Now let’s create a user service (also a dummy one, merely redirecting to our repository).</p>
<pre name="code" class="scala">
class UserService {
def authenticate(username: String, password: String): User =
userRepository.authenticate(username, password)
def create(username: String, password: String) =
userRepository.create(new User(username, password))
def delete(user: User) = All is statically typed.
userRepository.delete(user)
}
</pre>
<p>Here you can see that we are referencing an instance of the <code>UserRepository</code>. This is the dependency that we would like to have injected for us.</p>
<p>Ok. Now the interesting stuff starts. Let’s first wrap the <code>UserRepository</code> in an enclosing trait and instantiate the user repository there.</p>
<pre name="code" class="scala">
trait UserRepositoryComponent {
val userRepository = new UserRepository
class UserRepository {
def authenticate(user: User): User = {
println("authenticating user: " + user)
user
}
def create(user: User) = println("creating user: " + user)
def delete(user: User) = println("deleting user: " + user)
}
}
</pre>
<p>This simply creates a component namespace for our repository. Why? Stay with me and I’ll show you how to make use of this namespace in a second.</p>
<p>Now let’s look at the <code>UserService</code>, the user of the repository. In order to declare that we would like to have the <code>userRepository</code> instance injected in the <code>UserService</code> we will first do what we did with the repository above; wrap the it in an enclosing (namespace) trait and use a so-called <a href="http://www.scala-lang.org/node/124">self-type annotation</a> to declare our need for the <code>UserRepository</code> service. Sounds more complicated than it is. Let’s look at the code.</p>
<pre name="code" class="scala">
// using self-type annotation declaring the dependencies this
// component requires, in our case the UserRepositoryComponent
trait UserServiceComponent { this: UserRepositoryComponent =>
val userService = new UserService
class UserService {
def authenticate(username: String, password: String): User =
userRepository.authenticate(username, password)
def create(username: String, password: String) =
userRepository.create(new User(username, password))
def delete(user: User) = userRepository.delete(user)
}
}
</pre>
<p>The self-type annotation we are talking about is this code snippet:</p>
<pre name="code" class="scala">
this: UserRepositoryComponent =>
</pre>
<p>If you need to declare more than one dependency then you can compose the annotations like this:</p>
<pre name="code" class="scala">
this: Foo with Bar with Baz =>
</pre>
<p>Ok. Now we have declared the <code>UserRepository</code> dependency. What is left is the actual wiring.</p>
<p>In order to do that the only thing we need to do is to merge/join the different namespaces into one single application (or module) namespace. This is done by creating a “module” object composed of all our components. When we do that all wiring is happening automatically.</p>
<pre name="code" class="scala">
object ComponentRegistry extends
UserServiceComponent with
UserRepositoryComponent
</pre>
<p>One of the beauties here is that all wiring is statically typed. For example, if we have a dependency declaration missing, if it is misspelled or something else is screwed up then we get a compilation error. This also makes it very fast.</p>
<p>Another beauty is that everything is immutable (all dependencies are declared as <code>val</code>).</p>
<p>In order to use the application we only need to get the “top-level” component from the registry, and all other dependencies are wired for us (similar to how Guice/Spring works).</p>
<pre name="code" class="scala">
val userService = ComponentRegistry.userService
...
val user = userService.authenticate(..)
</pre>
<p>So far so good?</p>
<p>Well, no. This sucks.</p>
<p>We have strong coupling between the service implementation and its creation, the wiring configuration is scattered all over our code base; utterly inflexible.</p>
<p>Let’s fix it.</p>
<p>Instead of instantiating the services in their enclosing component trait, let’s change it to an abstract member field.</p>
<pre name="code" class="scala">
trait UserRepositoryComponent {
val userRepository: UserRepository
class UserRepository {
...
}
}
</pre>
<pre name="code" class="scala">
trait UserServiceComponent {
this: UserRepositoryComponent =>
val userService: UserService
class UserService {
...
}
}
</pre>
<p>Now, we can move the instantiation (and configuration) of the services to the <code>ComponentRegistry</code> module.</p>
<pre name="code" class="scala">
object ComponentRegistry extends
UserServiceComponent with
UserRepositoryComponent
{
val userRepository = new UserRepository
val userService = new UserService
}
</pre>
<p>By doing this switch we have now abstracted away the actual component instantiation as well as the wiring into a single “configuration” object.</p>
<p>The neat thing is that we can here switch between different implementations of the services (if we had defined an interface trait and multiple implementations). But even more interestingly, we can create multiple “worlds” or “environments” by simply composing the traits in different combinations.</p>
<p>To show you what I mean, we’ll now create a “testing environment” to be used during unit testing.</p>
<p>Now, instead of instantiating the actual services we instead create mocks to each one of them. We also change the “world” to a trait (why, I will show you in a second).</p>
<pre name="code" class="scala">
trait TestEnvironment extends
UserServiceComponent with
UserRepositoryComponent with
org.specs.mock.JMocker
{
val userRepository = mock(classOf[UserRepository])
val userService = mock(classOf[UserService])
}
</pre>
<p>Here we are not merely creating mocks but the mocks we create are wired in as the declared dependencies wherever defined.</p>
<p>Ok, now comes the fun part. Let’s create a unit test in which we are mixing in the <code>TestEnvironment</code> mixin, which is holding all our mocks.</p>
<pre name="code" class="scala">
class UserServiceSuite extends TestNGSuite with TestEnvironment {
@Test { val groups=Array("unit") }
def authenticateUser = {
// create a fresh and clean (non-mock) UserService
// (who's userRepository is still a mock)
val userService = new UserService
// record the mock invocation
expect {
val user = new User("test", "test")
one(userRepository).authenticate(user) willReturn user
}
... // test the authentication method
}
...
}
</pre>
<p>This pretty much sums it up and is just one example on how you can compose your components in the way you want.</p>
<h3>Other alternatives</h3>
<p>Let’s now take a look at some other ways of doing DI in Scala. This post is already pretty long and therefore I will only walk through the techniques very briefly, but it will hopefully be enough for you to understand how it is done. I have based all these remaining examples on the same little dummy program to make it easier to digest and to compare (taken from some discussion found on the Scala User mailing list). In all these examples you can just copy the code and run it in the Scala interpreter, in case you want to play with it.</p>
<p><strong>Using structural typing</strong></p>
<p>This technique using <a href="http://scala.sygneca.com/patterns/duck-typing-done-right">structural typing</a> was posted by Jamie Webb on the Scala User mailing list some time ago. I like this approach; elegant, immutable, type-safe.</p>
<pre name="code" class="scala">
// =======================
// service interfaces
trait OnOffDevice {
def on: Unit
def off: Unit
}
trait SensorDevice {
def isCoffeePresent: Boolean
}
// =======================
// service implementations
class Heater extends OnOffDevice {
def on = println("heater.on")
def off = println("heater.off")
}
class PotSensor extends SensorDevice {
def isCoffeePresent = true
}
// =======================
// service declaring two dependencies that it wants injected,
// is using structural typing to declare its dependencies
class Warmer(env: {
val potSensor: SensorDevice
val heater: OnOffDevice
}) {
def trigger = {
if (env.potSensor.isCoffeePresent) env.heater.on
else env.heater.off
}
}
class Client(env : { val warmer: Warmer }) {
env.warmer.trigger
}
// =======================
// instantiate the services in a configuration module
object Config {
lazy val potSensor = new PotSensor
lazy val heater = new Heater
lazy val warmer = new Warmer(this) // this is where injection happens
}
new Client(Config)
</pre>
<p><strong>Using implicit declarations</strong></p>
<p>This approach is simple and straight-forward. But I don’t really like that the actual wiring (importing the implicit declarations) is scattered and tangled with the application code.</p>
<pre name="code" class="scala">
// =======================
// service interfaces
trait OnOffDevice {
def on: Unit
def off: Unit
}
trait SensorDevice {
def isCoffeePresent: Boolean
}
// =======================
// service implementations
class Heater extends OnOffDevice {
def on = println("heater.on")
def off = println("heater.off")
}
class PotSensor extends SensorDevice {
def isCoffeePresent = true
}
// =======================
// service declaring two dependencies that it wants injected
class Warmer(
implicit val sensor: SensorDevice,
implicit val onOff: OnOffDevice) {
def trigger = {
if (sensor.isCoffeePresent) onOff.on
else onOff.off
}
}
// =======================
// instantiate the services in a module
object Services {
implicit val potSensor = new PotSensor
implicit val heater = new Heater
}
// =======================
// import the services into the current scope and the wiring
// is done automatically using the implicits
import Services._
val warmer = new Warmer
warmer.trigger
</pre>
<p><strong>Using Google Guice</strong></p>
<p>Scala works nicely with separate DI frameworks and early on we were using <a href="http://code.google.com/p/google-guice/">Google Guice</a>. You can use Guice in many different ways, but here we will discuss a slick technique based on a <code>ServiceInjector</code> mixin that my Jan Kriesten showed me.</p>
<pre name="code" class="scala">
// =======================
// service interfaces
trait OnOffDevice {
def on: Unit
def off: Unit
}
trait SensorDevice {
def isCoffeePresent: Boolean
}
trait IWarmer {
def trigger
}
trait Client
// =======================
// service implementations
class Heater extends OnOffDevice {
def on = println("heater.on")
def off = println("heater.off")
}
class PotSensor extends SensorDevice {
def isCoffeePresent = true
}
class @Inject Warmer(
val potSensor: SensorDevice,
val heater: OnOffDevice)
extends IWarmer {
def trigger = {
if (potSensor.isCoffeePresent) heater.on
else heater.off
}
}
// =======================
// client
class @Inject Client(val warmer: Warmer) extends Client {
warmer.trigger
}
// =======================
// Guice's configuration class that is defining the
// interface-implementation bindings
class DependencyModule extends Module {
def configure(binder: Binder) = {
binder.bind(classOf[OnOffDevice]).to(classOf[Heater])
binder.bind(classOf[SensorDevice]).to(classOf[PotSensor])
binder.bind(classOf[IWarmer]).to(classOf[Warmer])
binder.bind(classOf[Client]).to(classOf[MyClient])
}
}
// =======================
// Usage: val bean = new Bean with ServiceInjector
trait ServiceInjector {
ServiceInjector.inject(this)
}
// helper companion object
object ServiceInjector {
private val injector = Guice.createInjector(
Array[Module](new DependencyModule))
def inject(obj: AnyRef) = injector.injectMembers(obj)
}
// =======================
// mix-in the ServiceInjector trait to perform injection
// upon instantiation
val client = new MyClient with ServiceInjector
println(client)
</pre>
<p>That sums up what I had planned to go through in this article. I hope that you have gained some insight in how one can do DI in Scala, either using language abstractions or a separate DI framework. What works best for you is up to your use-case, requirements and taste.</p>
<p><strong>Update: </strong></p>
<p>Below I have added a Cake Pattern version of the last example for easier comparison between the different DI strategies. Just a note, if you compare the different strategies using this naive example then the Cake Pattern might look a bit overly complicated with its nested (namespace) traits, but it really starts to shine when you have a less then trivial example with many components with more or less complex dependencies to manage.</p>
<pre name="code" class="scala">
// =======================
// service interfaces
trait OnOffDeviceComponent {
val onOff: OnOffDevice
trait OnOffDevice {
def on: Unit
def off: Unit
}
}
trait SensorDeviceComponent {
val sensor: SensorDevice
trait SensorDevice {
def isCoffeePresent: Boolean
}
}
// =======================
// service implementations
trait OnOffDeviceComponentImpl extends OnOffDeviceComponent {
class Heater extends OnOffDevice {
def on = println("heater.on")
def off = println("heater.off")
}
}
trait SensorDeviceComponentImpl extends SensorDeviceComponent {
class PotSensor extends SensorDevice {
def isCoffeePresent = true
}
}
// =======================
// service declaring two dependencies that it wants injected
trait WarmerComponentImpl {
this: SensorDeviceComponent with OnOffDeviceComponent =>
class Warmer {
def trigger = {
if (sensor.isCoffeePresent) onOff.on
else onOff.off
}
}
}
// =======================
// instantiate the services in a module
object ComponentRegistry extends
OnOffDeviceComponentImpl with
SensorDeviceComponentImpl with
WarmerComponentImpl {
val onOff = new Heater
val sensor = new PotSensor
val warmer = new Warmer
}
// =======================
val warmer = ComponentRegistry.warmer
warmer.trigger
</pre><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/grubbel?a=xqYFqRt8Mlg:ehIGNcNm2fw:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/grubbel?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/grubbel?a=xqYFqRt8Mlg:ehIGNcNm2fw:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/grubbel?i=xqYFqRt8Mlg:ehIGNcNm2fw:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/grubbel?a=xqYFqRt8Mlg:ehIGNcNm2fw:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/grubbel?i=xqYFqRt8Mlg:ehIGNcNm2fw:F7zBnMyn0Lo" border="0"></img></a>
</div>
Jonas Boner's complete blog can be found at: http://jonasboner.com/
About Jonas Boner
Jonas Bonér is working at Terracotta Inc. with a focus on strategy, product development & architecture and technical evangelism. Terracotta provides software that delivers enterprise-class infrastructure services to applications transparently at runtime with minimal required application code changes.
Prior to Terracotta, Jonas was a senior software engineer at the JRockit team at BEA Systems, where he was working on runtime tools, JVM support for AOP and technology evangelism.
He is the founder of the AspectWerkz AOP framework and committer to the Eclipse AspectJ 5 project. Jonas is a frequent speaker on AOP and
other emerging technologies (JavaOne, JAOO, eWorld, Java Pro Live!, Javapolis, AOSD conferences etc.).
Why Attend the NFJS Tour?
- » Cutting-Edge Technologies
- » Agile Practices
- » Peer Exchange
Current Topics:
- Languages on the JVM: Scala, Groovy, Clojure
- Enterprise Java
- Core Java, Java 8
- Agility
- Testing: Geb, Spock, Easyb
- REST
- NoSQL: MongoDB, Cassandra
- Hadoop
- Spring 4
- Cloud
- Automation Tools: Gradle, Git, Jenkins, Sonar
- HTML5, CSS3, AngularJS, jQuery, Usability
- Mobile Apps - iPhone and Android
- More...