Dependency Injection in Scala: Extending the Cake Pattern

5 Flares 5 Flares ×

Continuing the mini-series on Dependency Injection (see my previous blogs: problems with DI, assisted inject for CDI and improving assisted inject), I took a look at how DI is handled in Scala.

There are several approaches, one of the most interesting being the Cake Pattern. It is a DI solution that uses only native language features, without any framework support. For a good introduction see either Jonas Boner’s blog (on which this post is largerly based) or Martin Odersky’s paper Scalable Component Abstractions.

I would like to extend the Cake Pattern to allow defining dependencies which need some user-provided data to be constructed (like in autofactories/assisted inject).

The Cake Pattern: interfaces

But let’s start with an example of the base pattern. Let’s say that we have a User class,

1
sealed case class User(username: String)

and that we want to create a UserRepository service. Using the Cake Pattern, first we create the “interface”:

1
2
3
4
5
6
7
trait UserRepositoryComponent { // For expressing dependencies
   def userRepository: UserRepository // Way to obtain the dependency
 
   trait UserRepository { // Interface exposed to the user
      def find(username: String): User
   }
}

We have three important things here:

  • the UserRepositoryComponent trait will be used to express dependencies. It contains the component definition, consiting of:
  • a way to obtain the dependency: the def userRepository method (could also be a val, but why a def is better I’ll explain later)
  • the interface itself, here a UserRepository trait, which gives the functionality of locating users by username

The Cake Pattern: implementations

An implementation of a component looks pretty similar:

1
2
3
4
5
6
7
8
9
10
11
trait UserRepositoryComponentHibernateImpl
                extends UserRepositoryComponent {
   def userRepository = new UserRepositoryImpl 
 
   class UserRepositoryImpl extends UserRepository {
      def find(username: String): User = {
         println("Find with Hibernate: " + username)
         new User(username)
      }
   }
}

Nothing special here. The component implementation extends the “interface” component trait. This brings into scope the UserRepository trait, which can be implemented.

Using dependencies

How can one component/service say that it depends on another? Scala’s self-type annotations are of much use here. For example, if a UserAuthorization component requires the UserRepository, we can write this as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Component definition, as before
trait UserAuthorizationComponent {
   def userAuthorization: UserAuthorization
 
   trait UserAuthorization {
      def authorize(user: User)
   }
}
 
// Component implementation
trait UserAuthorizationComponentImpl 
                extends UserAuthorizationComponent {
   // Dependencies
   this: UserRepositoryComponent =>
 
   def userAuthorization = new UserAuthorizationImpl
 
   class UserAuthorizationImpl extends UserAuthorization {
      def authorize(user: User) {
         println("Authorizing " + user.username)
         // Obtaining the dependency and calling a method on it
         userRepository.find(user.username)
      }
   }
}

The important part here is this: UserRepositoryComponent =>. By this code fragment we specify that the UserAuthorizationComponentImpl requires some implementation of the UserRepositoryComponent. This also brings the content of the UserRepositoryComponent into scope, so both the method to obtain the user repository and the UserRepository trait itself are visible.

Wiring

How do we wire different components together? Again quite easily. For example:

1
2
3
4
val env = new UserAuthorizationComponentImpl 
            with UserRepositoryComponentHibernateImpl
 
env.userAuthorization.authorize(User("1"))

First we need to construct the environment, by combining all of the components implementations that we want to use into a single object. Next, we can call methods on the environment to obtain services.

What about testing? Also easy:

1
2
3
4
5
val envTesting = new UserAuthorizationComponentImpl 
            with UserRepositoryComponent {
   def userRepository = mock(classOf[UserRepository])
}
envTesting.userAuthorization.authorize(User("3"))

Here we have mocked the user repository, so we can test the UserAuthorizationComponentImpl in isolation.

defs over vals

Why are defs in the component definition better as the way to obtain the dependency? Because if you use a val, all implementations are locked and have to provide a single dependency instance (a constant). With a method, you can return different values on each invocation. For example, in a web environment, this is a great way to implement scoping! The method can read from the request or session state. Of course, it is still possible to provide a singleton. Or a new instance of the dependency on each invocation.

Dependencies that need user data

Finally, we arrive to the main point. What if our dependencies need some data at runtime? For example, if we wanted to create a UserInformation service, which wraps a User instance?

Well, who said the the methods by which we obtain the dependencies need to be parameterless?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Interface
trait UserInformationComponent {
   // What is needed to create the component
   def userInformation(user: User)
 
   trait UserInformation {
      def userCountry: Country
   }
}
 
// Implementation
trait UserInformationComponentImpl
                extends UserInformationComponent {
   // Dependencies
   this: CountryRepositoryComponent =>
 
   def userInformation(user: User) = new UserInformationImpl(user)
 
   class UserInformationImpl(val user: User) extends UserInformation {
      def userCountry: Country {
         // Using the dependency
         countryRepository.findByEmail(user.email)
      }
   }
}
 
// Usage
val env = new UserInformationComponentImpl 
                        with CountryRepositoryComponentImpl    
env.userInformation(User("someuser@domain.pl")).userCountry

Isn’t this better than passing the User instance as a method parameter?

Using the Cake Pattern, creating stateful dependencies, which can be created at run-time with user-provided data, and still depend on other components is a breeze. This is similar to a factory method, however with much less noise.

The good and the bad

The good:

  • no framework required, using only language features
  • type safe – a missing dependency is found at compile-time
  • powerful – “assisted inject”, scoping possible by implementing the dependency-providing method appropriately

The bad:

  • quite a lot of boilerplate code: each component has a component interface, implementation, service interface and service implementation

However, I don’t think defining all four parts is always necessary. If there’s only one implementation of a component, you can combine the component interface and implementation into one, and if there’s a need, refactor later.

Adam

  • http://agilewiki.org/ Bill La Forge

    You missed a big point, that we use extensively–by using def’s in place of val’s, it is possible to extend a component. An the overriding access method can also reflect the extending component.

    So you can have a base component which is widely known and a subcomponent known by a few other components. And those few components which are aware of the subcomponent can use its additional methods because the dependency (this: X =>) identifies the subcomponent.

    In AgileWiki we use the cake pattern both for the major components of the system and for the elements of our DOM.

    –b

  • http://www.warski.org Adam Warski

    True, another point in favor of defs vs vals. Thanks for the comment.

    Adam

  • Stephen

    If you’re doing a DI mini-series, you might also take a look at:

    http://sonymathew.blogspot.com/2009/11/context-ioc-revisited-i-wrote-about.html

    It is the same sort of “framework-less DI” approach of Cake, but just inner-interfaces instead of container-traits/self-types. Which to me makes it simpler to understand.

    Personally, I don’t have a lot of outside-my-app code reuse, so have gotten by nicely with a single ApplicationContext interface, which is like the union of each service’s inner-interface in Context IoC merged into one.

  • http://agilewiki.org/ Bill La Forge

    When building a program of singleton components, initialization order gets critical. The obvious answer is to make the factory methods lazy. I.E. Create the nested object on request, and only if not already created.

    This makes it possible to start building a library of components that you can use in composing new programs.

  • http://www.warski.org Adam Warski

    Thanks for the link! Looks interesting.

    Adam

  • Tymur Porkuian

    If we have two components like these:

    1) new UserAuthorizationComponentImpl with UserRepositoryComponentHibernateImpl

    2) new UserSomethingElseComponentImpl with UserRepositoryComponentHibernateImpl

    Wouldn’t “new UserRepositoryImpl” be called twice?

  • http://www.warski.org Adam Warski

    Yes. Typically you create components once (when you create the application environment). Separately of course in tests.

    Another thing is that if you have def userRepository = new UserRepositoryImpl, then a new URI will be created on each dependency lookup. But you here you have the possibility to implement this as a val, making the dependency a singleton (in a single component intstance).

    Adam

  • Vladimir

    At first, I want to thank you for the most actual and simple description of this pattern.

    I think that the previous commentor meant the other thing, look, when you’re using one component in two different components, two instances of the component used will be created.

    For instance, I have DatabaseComponent and two components using it UsersComponent and SalesComponent. I have Database object within DatabaseComponent created twice despite of “val database = new H2()” declaration.

    The simple solution I am maintaining now is to hold static collection (object) of already created instances and take database instances from there at “def database”. It is not going to be a good solution. It would be great if you could suggest something better.

    Thank you.
    Vladimir.

  • http://www.warski.org Adam Warski

    Well a Database instance will be created as many times as the component is created – typically, you should have only one instance of every component.
    That is, when you start you application, you do sth like this:

    val env = new UsersComponent with SalesComponent with DatabaseComponent

    and for example store the environment in some static/non-static variable available from the UI (all depends on what the application is).
    Then when a user presses a button, you just do:

    env.sales.processSale(…)

    You define the dependencies using self-types:

    class SalesComponent { this: DatabaseComponent => … }
    class UsersComponent { this: DatabaseComponent => … }

    Is it something similar to what you have?

    Adam

  • Vladimir

    Yes, almost exactly.

    My UI is the Circumflex RequestRouter and it is the environment itself:

    class RequestProcessor extends RequestRouter
    with H2Component // implementation of the DatabaseComponent
    with DatabaseSalesComponent
    with DatabaseUsersComponent {

    }

    trait DatabaseSalesComponent extends SalesComponent {
    this: DatabaseComponent =>

    }

    trait DatabaseUsersComponent extends UsersComponent {
    this: DatabaseComponent =>

    }

    trait H2Component extends DatabaseComponent {
    def database: Database = {
    // XXX:
    H2Databases.synchronized {
    H2Databases.get match {
    case Some(database) => database.asInstanceOf[Database]
    case None => {
    H2Databases.set(new H2()).asInstanceOf[Database]
    }
    }
    }
    }
    }

    Doing this I have H2Component.database called a few times almost simultaneously. It reminds me the http://en.wikipedia.org/wiki/Diamond_problem.

  • http://www.warski.org Adam Warski

    I’m not familiar with Circumflex RequestRouter but it seems that new component instances may be created on each request (as the class – RequestProcessor – suggest). Then your database will be initialized each time a request is started.

    I think that instead, you should have an “application-scoped” environment variable, which mixes in all the components and is instantiated once at the application start.

    Adam

  • Vladimir

    RequestRouter is like a Servlet (hence it is created just once).
    I have multiple Database instances created after one “Servlet” creation.

  • http://www.warski.org Adam Warski

    Are you 100% sure that there’s only one RequestRouter instance? Otherwise I don’t have much ideas now, that would need some debugging with the code :)

    Adam

  • Pingback: links for 2011-02-01 « Xume linklog

  • http://pl.linkedin.com/in/przemyslawpokrywka Przemek Pokrywka

    Hi Adam. You’ve become a victim of a popular misconception about the Cake Pattern, because DI is actually a small part of this rather complex pattern – I’ve blogged about that in more details here:

    http://biased-and-fair-software-development.blogspot.com/2011/01/slicing-cake.html

    Your post may suggest, that using Scala to do DI requires a lot of boilerplate, however this is false, because you shouldn’t use the full Cake, when all you need is DI. Self-types will do and they are a lot simpler than the full pattern.

  • http://www.warski.org Adam Warski

    Most probably I didn’t express my thoughts clearly (as often happens).

    First of all, self-types: in the cake pattern they are really used to achieve auto-wiring (which is a feature of a DI *container*). Core/manual DI is in fact just using abstraction to get your dependencies.

    Boilerplate: There is some in the full form: if you want an interface/impl separation. As often happens, if you have a module interface (exposed to the public), and a module implementation (hidden).

    Adam

  • Pingback: DI in Scala: Cake Pattern pros & cons @ Blog of Adam Warski

  • Pingback: Modules, modules, modules … @ Blog of Adam Warski

  • Pingback: Constructor injection in Scala | subtype

  • Pingback: The Cake walk « Tim Gilbert's Blog

  • Pingback: i&t

  • John Murray

    Your link to the original blog post on the Cake pattern seems to be incorrect. Remove the “.html” from the end of the link and that should fix things.

  • http://www.warski.org/ Adam Warski

    Fixed, thanks!

5 Flares Twitter 4 Facebook 0 Google+ 1 LinkedIn 0 Email -- 5 Flares ×