Dependency injection vs. Cake pattern

Typically, the argument comes down to is Spring Framework better than Play Framework?, is Lift better than Spring Framework?, is Grails better than everything else?. In this post, we will explore the world of contemporary JEE applications, paying extra attention to the usual DI frameworks and frameworks that implement the Cake pattern.

For more detailed discussion of the Cake pattern, see Mark’s Cake pattern in depth

Before we rush into condemning one or the other, we feel that what you need to think about what kind of application you’re writing. We’ll go as far as to say that if you need JMS, JMX, scheduling, WebFlow, …, then you should stick with the Spring Framework. By all means implement the components that make up your Spring application in Scala! If these JEE dinosaurs do not come anywhere near your application, then Play 2 in Scala, using the Cake pattern might just be the perfect thing for you!

Dependency injection

Spring Framework, we have multiple modules; each module contains the code that is appropriate for the tier that the module implements. The code means the Java interfaces & their implementations as well as the Spring configuration (typically in the META-INF/spring/module-context.xml file). The types of the dependencies are checked at compile-time: if we haven’t defined the UserService interface, for example, and tried to use it in the UserController, the compilation would fail. However, the compiler cannot check that the appropriate implementation of the UserService will be available to be injected to the UserController at runtime. The application is loosely coupled, which is good, but we must supply appropriate metadata (the annotations & the Spring XML) so that Spring can construct the application.

Dependency-injection applications must provide valid metadata so that the DI framework can construct their components & satisfy the dependencies between them at runtime.

Let’s write a trivial application that contains a repository and a service. We will follow the recommended code to interfaces approach. This gives us the following code in the repository:

public interface UserRepository {
  List<User> findAll();
}

@Repository
public class ORMUserRepository implements UserRepository {
  private SessionFactory sessionFactory = // construct the SF
  public List<User> findAll() {
    // not real Hibernate code, but close enough
    return sessionFactory.getCurrentSession().query(...);
  }
}

We now combine this code with the Spring context file, which instructs the DI core to create the ORMUserRepository component.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="...">

  <context:annotation-config/>
  <context:component-scan base-package="...repository"/>

<beans/>

We implement the code in the services in using a similar pattern: we have the UserService interface and we implement it in some class. Crucially, we will rely on Spring to inject a valid implementation of the UserRepository to the instance of the DefaultUserService at runtime, using the configuration we provide.

public interface UserService {
  List<User> findAll();
}

@Service
public class DefaultUserService implements UserService {
  private final UserRepository userRepository;

  @Autowired
  public DefaultUserService(final UserRepository userRepository) {
    if (userRepository == null)
      throw new IllegalArgumentException("Off Santa's list!");
    this.userRepository = userRepository;
  }

  public List<User> findAll() {
    return this.userRepository.findAll();
  }
}

The services module needs its configuration, too. And so, we give the familiar /META-INF/spring/module-context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="...">

  <context:annotation-config/>
  <context:component-scan base-package="...services"/>

<beans/>

We have the components and the metadata. All we need now is to let Spring Framework create all the components. In other words, the ApplicationContext implementation that the Spring Framework provides will become our application:

public class App {

  public static void main(String[] args) {
    ApplicationContext application =
      new ClassPathXmlApplicationContext(
        "classpath*:/META-INF/spring/module-context.xml"));

    UserService service = application.getBean(UserService.class);

    // use it!
    service.findAll();
  }
}

The Spring Framework became our application! All we have is a shell that boots-up Spring, as shown on Figure 1.

Figure 1. Spring DI application

The Cake Pattern

If you are writing new Scala application, you might not want to take in all the baggage that Spring Framework brings with it. (Slf4j, anyone?)

You would typically use the Cake Pattern to achieve something akin to dependency injection. Jonas Boner explained the Cake Pattern in one of his infamous Real-World Scala Series blog post. What we would try here is to use the Cake pattern to implement the same DI scenario in Scala and then compare that with its corresponding Spring implementation. In doing that we would hope to clear out some of the misty clouds over Scala and DI.

We also want to have a repository and a service, but without the XML configuration file. So, we need not only the repository and service interfaces themselves, but we also need components in which they “live”. Starting with the repository, we have:

trait UserRepositoryComponent {
  def userRepository : UserRepository

  trait UserRepository {
    def findAll: List[User]
  }
}

trait ORMUserRepositoryComponent extends UserRepositoryComponent {
  def userRepository = new ORMUserRepository(sf) //we need to actually instantiate the session factory here

  class ORMUserRepositry(val sf: SessionFactory) extends UserRepository {
    def findAll: List[User] = sf.query(...)
  }
}

Good. We have the repository tier interface the UserRepositoryComponent provides a way to get to the UserRepository instance. We have also shown an implementation of the repository interfaces: the ORMUserRepositoryComponent‘s userRepository function gives an ORM-based implementation of the UserRepository.

Now, we would like to define the interface to the services tier:

trait UserServiceComponent {
  def userService: UserService

  trait UserService {
    def findAll: List[User]
  }
}

OK, we have the repository interface & implementation and the service interface. The implementation of the service will need to have access to some implementation of the repository interface. Naturally, we don’t want to specify which concrete repository interface implementation we want to use. In fact, we would like to let the users of our code specify arbitrary implementation of the repository interfaces. Good thing we have the composition inheritance!

trait DefaultUserServiceComponent extends UserServiceComponent {
  this: UserRepositoryComponent => 

  def userService = new DefaultUserService

  class DefaultUserService extends UserService {
    def findAll = userRepository.findAll
  }
}

Notice in particular this: UserRepositoryComponent =>. This means that the instance of DefaultUserServiceComponent depends on the UserRepositoryComponent. (It’s a little more subtle than that, but for the purposes of this post it will do.) This is in function equivalent to Spring’s @Autowired public DefaultUserService(final UserRepository userRepository). Just like the Spring application we have created, the Scala code does nothing yet. We need to wire it together in some kind of application!

object Application {
  val userServiceComponent = new DefaultUserServiceComponent with ORMUserRepositoryComponent
  val userService = userServiceComponent.userService
}

val userService = Application.userService
userService.findAll

In Scala, there is no ApplicationContext, so we have to implement our own application. But where’s the dependency injection? Here: new DefaultUserServiceComponent with ORMUserRepositoryComponent, notice the with ORMUserRepositoryComponent. Figure 2 shows the Scala application.

Figure 2. Scala DI

The comparison

As you can see, the two approaches are completely different in their implementation. DI in Spring Framework is a runtime business and the component separation is left to the way in which we structure the configuration files. DI in Scala/Cake is a compile-time business with sharper component separation. Figure 3 gives the overall comparison.

Figure 3. The big picture

Summary

As we have shown here, both pattern represent in function a kind of dependency injection & management. However, the crucial difference is that the dependency injection (in Spring Framework) is useful to manage components, leaving us to carefully design the components. The Cake Pattern allows us to inject & manage functionality. We use traits to assemble components with the functionality we require. It is a different approach to design. Let me repeat:

In plain dependency injection, we create components and we assemble these components together to form an application.
Using the Cake Pattern, we create pieces of functionality and we assemble the functionality to form the application.

This entry was posted in Ani's Blog, Jan's Blog and tagged , , , , , . Bookmark the permalink.

11 Responses to Dependency injection vs. Cake pattern

  1. Rossen Stoyanchev says:

    It would be useful to show the SessionFactory being injected as it would be in a Spring application:
    @Autowred private SessionFactory sessionFactory;

    Rather than:
    private SessionFactory sessionFactory = // construct the SF

    The use of the sessionFactory would then be:
    sessionFactory.getCurrentSession().query(…); // get the “current” (transactional) session

    Rathern than:
    sessionFactory.getSession().query(…);

    It’s also worth pointing out that with Java-based Spring configuration you do get compile-time verification.

  2. Jan Machacek says:

    Thanks for your comments–and good points. We’re actually cooking up a video with matching Git repository that shows complete sources for both. I’ll try to show off Spring 3.1′s XML-less configuration (including no web.xml!).

    For now, we’ll add the “Current” bit in :)

  3. Eric P says:

    How does the Cake pattern handle AOP, like the @Transactional annotation given above?

  4. Jan Machacek says:

    Hi Eric,

    It doesn’t–the Cake pattern only allows you to compose behaviour to form the system you want. Your question is very valid when you’re building typical JEE applications.

    There are two approaches:
    * use transactionally function that surrounds the code in the TX management:
    def myMethod(arbitrary: Arguments): Result = transactionally {
    eval-to-Result
    }

    * you can use AOP (either the standard Java AspectJ or Scala AspectJ). What remains is to implement the aspects. You’d probably end up with something like:
    @transactional
    def myMethod(arbitrary: Arguments): Result = {
    eval-to-Result
    }

    The two approaches give the same results, but the implementation details are completely different. In the transactionally[R](f: => R): R approach, you will need to mix-in something that provides the transactional function; In the AOP approach, you will need to implement an around advice in the transactional aspect.

    But I bet I haven’t fully answered your question! How do you inject the appropriate transaction manager and how do you keep the whole system loosely coupled? And this is where the Cake Pattern can help. We will require a dependency on something that can give the appropriate transaction manager; we will use this transaction manager in the advice or in the trait that contains the transactionally function.

    How would we do that–well, that’s for a longer discussion. I will post describing both solutions in the next couple of days!

  5. Christopher Hunt says:

    I think your title is misleading, particularly given your summary statements and how DI is done using Spring vs Scala/Cake.

  6. Jan Machacek says:

    I’ve chosen this title because it is the question I get asked most often: “I want to have DI. I’ve heard about the Cake pattern. I wonder how it compares with the ‘traditional’ dependency injection.”

    What would you suggest?

  7. Eric P says:

    Another interesting use case is scoped injection, ie. singleton vs prototype vs request vs session, etc.

  8. Hi Jan,

    Your solutions (transactionally function; AOP) both require you to effectively write a framework akin to Spring or a JEE container…

    To me, the Cake Pattern is not useful in an enterprise application.

  9. Jan Machacek says:

    That’s why we still like Spring for traditional JEE applications. If you’re considering pure Scala applications, you should look for appropriate Scala frameworks like Akka or Play [2].

    What is important here is that Scala language features allow you to easily compose functionality. Java leaves you to your own devices. Nota bene that that’s not saying that Java is bad or that Scala is better. They are simply different approaches with similar consequences, but completely different mechanisms.

  10. Pingback: Spring and Scala | Cake Solutions Team Blog

  11. Pingback: Scala Play2 Application | JointEffort

Leave a Reply