Java and JBoss related stuff
RSS icon Home icon
  • Dependency Injection in Scala: Extending the Cake Pattern

    Posted on December 14th, 2010 Adam Warski 20 comments

    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,

    sealed case class User(username: String)
    

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

    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:

    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:

    // 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:

    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:

    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?

    // 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

  • Improving autofactories/assisted inject

    Posted on December 7th, 2010 Adam Warski 8 comments

    In my two previous posts I wrote about some problems with DI and a solution to part of those problems: assisted inject (as known in Guice)/autofactories (my implementation for CDI). Some problems remained however; for example in the bean constructor, the dependencies (or the environment) are mixed with the data obtained from the factory. That is also why the @FactoryParameter annotation is required.

    This shortcoming can be quite easily overcome, by using field injection for dependencies, and constructors for data from the factory. How does it work? And what about testing? Read on :).

    The problem

    The main goal is to be able to define beans, in which dependency injection works as usual and which can be constructed on-demand with user-supplied data. Something more object-oriented than creating stateless beans where the user-supplied data is passed in method parameters, and something with less boilerplate than implementing factories.

    Just to remind, as an example throughout the posts I am using a ProductShipper bean, which needs some outside dependencies (“services”, for example a PriceCalculatorService), and must be provided with a Product object.

    The solution

    The base interface remains the same:

    public interface ProductShipper {
         void ship(User target);
    
         interface Factory {
              ProductShipper create(Product product);
         }
    }
    

    Again just to remind, the biggest advantage of the nested factory interface is that it is clear what is needed to create a bean (here the shipper), and we spare some typing as we don’t need to write a long class name for the factory.

    Now for the implementation. Before both outside dependencies and factory parameters were passed in the constructor. Now, we’ll use field injection, making a clear separation:

    @CreatedWith(ProductShipper.Factory.class)
    public class ProductShipperImpl implements ProductShipper {
         @Inject
         private PriceCalculatorService priceCalculator;
    
         @Inject
         private TransportService transport;
    
         private final Product product;
    
         public ProductShipperImpl(Product product) {
              this.product = product;
         }
    
         // implement shipTo(User)
    }
    

    Looks quite simple. The only thing we needed to do is to specify the factory for the bean (@CreatedWith) and provide a constructor matching the parameters of the factory. Hence, one problem remains: there’s no compile-time checking (only deployment-time), if the arguments lists match. So there’s still room for improvement! :)

    Usage

    To use the autofactory, you simply have to inject the factory bean. The important thing is that you don’t have to implement the factory bean by hand, it is auto-generated. For example:

    public class Test {
         @Inject
         private ProductShipper.Factory productShipperFactory;
    
         public void test() {
              ProductShipper shipper = productShipperFactory
                   .create(new Product("butter"));
    
              shipper.shipTo(new User("me"));
         }
    }
    

    Testing

    Many would argue that using field injection instead of constructor injection makes the bean much harder to test. But we can improve that quite easily, thanks to the CDIInjector helper class, from softwaremill-util. Here’s how we can test our bean:

    import static pl.softwaremill.common.util.CDIInjector.*;
    
    public class ProductShipperTest {
         @Test
         public void testShipping() {
              Product product = ...;
              ProductShipper shipper = new ProductShipperImpl(product)
    
              // Here's the important part: "dependency injection"
              into(shipper).inject(mockPriceCalculator, mockService);
    
              // Test goes on ...
         }
    }
    

    So instead of setting the fields explicitly via reflection (where we would have to write the field names), we just need to provide the target of injection (into(shipper)), and the objects that we want to be injected (.inject(...)). We can also inject objects with qualifiers.

    As always the code is available on github, as part of the softwaremill-common project (softwaremill-cdi release 9 and softwaremill-util release 13). The autofactories implementation is a portable extension (supports both styles: constructor and field/setter injection for dependencies), so all you need to do is to include the jar in your deployment.

    Adam