Dependency Injection @ scala


Scala is all about scalable,functional, immutability and most importantly less piece of code!
While building a framework using shared-nothing architecture, i came across the requirement of linking/referencing various modules(rather services) together.  Dependency injection was an obvious choice. Let’s take ahead this discussion with further exploration of Dependency Injection(DI) and various possible ways to accomplish it

Dependency Injection
It is basically providing the objects that an object needs (its dependencies) instead of having it construct them itself. It’s a very useful technique for testing, since it allows dependencies to be mocked or stubbed out.In other word, Dependency injection is a design pattern whose core principle is to separate behavior from dependency resolution.
There is an article by Martin Fowler that can provide more insights about DI.

Basically needs of DI are:
Unit testing
Mock Data Access Layer
Mock File System
Mock Email

Modular design
Multiple different  implementation
Upgradeable infrastructure

Separation of concern
Single Responsibility
Increase code Re-Use
Cleaner Modeling
Concurrent or independent development

To summarize, Dependency injection is a software design pattern in which any dependency object has should be provided that dependency instead of internally create with in an object.Actually this implementation Dependency Inversion Principle.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.

Let’s talk about developer business, i.e. implementation.

Talking about scala and DI, there are multiple alternatives are available, few popular ones are:

1.Constructor injection
2.Cake pattern
3.Google guice

Constructor injection
As the name says itself!, Declare all required dependencies for an object as constructor arguments. Great! but how does it work?Well, lets take on this with an example

A UserService class that take user information from database and do some operation.
So first we need to get data from database for that , define a class UserDAL which have create ,update, delete methods on database rows.

class UserDAL {
/* dummy data access layer */
def getUser(id: String): User = {
val user = User("12334", "testUser", "test@knoldus.com")
println("UserDAL: Getting user " + user)
user
}
def create(user: User) = {
println("UserDAL: creating user: " + user)
}
def delete(user: User) = {
println("UserDAL: deleting user: " + user)
}
}
// case class is simple class which some extra factory methods .User class have information about user.
case class User(id: String, name: String, email: String)

Now define a UserService class , UserService class have some operation on data those provided by UserDAL class. UserService class have dependency  on UserDAL so UserService class declare UserDAL class as a constructor parameter.

class UserService(userDAL: UserDAL) {
 def getUserInfo(id: String): User = {
val user = userDAL.getUser(id)
println("UserService: Getting user " + user)
user
}

def createUser(user: User) = {
userDAL.create(user)
println("UserService: creating user: " + user)
}

def deleteUser(user: User) = {
userDAL.delete(user)
println("UserService: deleting user: " + user)
}

Something wrong in design. Any guesses ? Take a closer look
class UserService(userDAL: UserDAL) {
You answer is violation of Dependency Inversion Principle.
The principle states:
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend on details. Details should depend on abstractions.

So we define a trait :

trait UserDALComponent {

def getUser(id: String): User
def create(user: User)
def delete(user: User)

}
class UserDAL extends UserDALComponent {
// a dummy data access layer that is not persisting anything
def getUser(id: String): User = {
val user = User("12334", "testUser", "test@knoldus.com")
println("UserDAL: Getting user " + user)
user
}
def create(user: User) = {
println("UserDAL: creating user: " + user)
}
def delete(user: User) = {
println("UserDAL: deleting user: " + user)
}

}
class UserService(userDAL: UserDALComponent) {

def getUserInfo(id: String): User = {
val user = userDAL.getUser(id)
println("UserService: Getting user " + user)
user
}

def createUser(user: User) = {
userDAL.create(user)
println("UserService: creating user: " + user)
}

def deleteUser(user: User) = {
userDAL.delete(user)
println("UserService: deleting user: " + user)
}

 

UserService depend on UserDALComponent(abstraction) not implemention.

Cake Pattern
The current strategy we are using is based on the so-called Cake Pattern. Cake Pattern is first explained in Martin Oderskys’ paper Scalable Component Abstractions (which is an excellent paper that is highly recommended) as the way he and his team structured the Scala compiler.Lets discuss by Example:

//Data access layer
trait UserDALComponent {
val userDAL: UserDAL

class UserDAL {
// a dummy data access layer
def getUser(id: String): User = {
val user = User("12334", "testUser", "test@knoldus.com")
println("UserDAL: Getting user " + user)
user
}
def create(user: User) = {
println("UserDAL: creating user: " + user)
}
def delete(user: User) = {
println("UserDAL: deleting user: " + user)
}

}
}

// User service which have Data Access Layer dependency

trait UserServiceComponent { this: UserDALComponent =>

val userService: UserService

class UserService {
def getUserInfo(id: String): User = {
val user = userDAL.getUser(id)
println("UserService: Getting user " + user)
user
}

def createUser(user: User) = {
userDAL.create(user)
println("UserService: creating user: " + user)
}

def deleteUser(user: User) = {
userDAL.delete(user)
println("UserService: deleting user: " + user)
}

}
}

Here class UserDAL are defined inside UserDALComponent trait with abstract reference variable of UserDAL . Question is that why reference variable is abstract. Because if you writing unit test of UserService then you can mock UserDAL easily.(By mocking framework). and second reason is  violation of Dependency Inversion Principle.

Create a object and extends UserService and UserDAL trait:
object User extends UserServiceComponent with UserDALComponent {
val userService = new UserService
val userDAL = new UserDAL // pass mocked UserDAL object for unit testing

}

Google Guice
Google Guice is an open source software framework for the Java platform released by Google under the Apache License.It provides support for dependency injection using annotations to configure Java objects.Dependency injection is a design pattern whose core principle is to separate behavior from dependency resolution.

Basically Google guice framework are made for java but we can use as well in scala.
sbt dependency  for Google guice;

 libraryDependencies +="com.google.inject" % "guice" % "3.0" 

Lets discuss by a Example:


// define a trait for database access layer
trait UserDALComponent {

def getUser(id: String): User
def create(user: User)
def delete(user: User)

}
class UserDAL extends UserDALComponent {
// a dummy data access layer
def getUser(id: String): User = {
val user = User("12334", "testUser", "test@knoldus.com")
println("UserDAL: Getting user " + user)
user
}
def create(user: User) = {
println("UserDAL: creating user: " + user)
}
def delete(user: User) = {
println("UserDAL: deleting user: " + user)
}
}
// User service which have Data Access Layer dependency

class UserService @Inject()(userDAL: UserDALComponent) {

def getUserInfo(id: String): User = {
val user = userDAL.getUser(id)
println("UserService: Getting user " + user)
user
}

def createUser(user: User) = {
userDAL.create(user)
println("UserService: creating user: " + user)
}

def deleteUser(user: User) = {
userDAL.delete(user)
println("UserService: deleting user: " + user)
}

}

Guice requeries a extra configuration class in which we bind trait with thier implementing class.

import com.google.inject.{ Inject, Module, Binder, Guice }
import com.knol.di.UserDALComponent
import com.knol.di.UserDAL
import com.google.inject.name.Names

class DependencyModule extends Module {

def configure(binder: Binder) = {
binder.bind(classOf[UserDALComponent]).to(classOf[UserDAL])

}
}

Guice take all responsibilty of depencencies and object creation.

val injector = Guice.createInjector(new DependencyModule)
val component = injector.getInstance(classOf[UserService]) //get UserService object

References:
1. A very popular blog on DI by Jonas boner.
2.Inversion of Control Containers and the Dependency Injection pattern By Martin Fowler.

Happy programming !!! Keep innovating :)

 

 

This entry was posted in Scala. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s