-
DI in Scala: Cake Pattern pros & cons
Posted on April 29th, 2011 28 commentsI’ve been looking at alternatives for java-style DI and DI containers which would use pure Scala; a promising candidate is the Cake Pattern (see my earlier blog post for information on how the Cake Pattern works). FP enthusiast also claim that they don’t need any DI frameworks, as higher-order functions are enough.
Recently Debasish Ghosh also blogged on a similar subject. I think his article is a very good introduction into the subject.
Below are some problems I encountered with the Cake Pattern. (Higher-order functions are coming up in the next post.) If you have solutions to any of them, let me know!
Parametrizing the system with a component implementation
First of all, it is not possible to parametrize a system with a component implementation. Supposing I have three components:
DatabaseComponent,UserRepositoryComponent,UserAuthenticatorComponentwith implementations, the top-level environment/entry point of the system would be created as follows:val env = new MysqlDatabaseComponentImpl with UserRepositoryComponent with UserAuthenticatorComponent
Now to create a testing environment with a mock database, I would have to do:
val env = new MockDatabaseComponentImpl with UserRepositoryComponent with UserAuthenticatorComponent
Note how much of the code is the same. This isn’t a problem with 3 components, but if there are 20? All of them but one have to be repeated just to change the implementation of one component. This clearly leads to quite a lot of code duplication.
Component configuration
Quite often a component needs to be configured. Let’s say I have a
UserAuthenticatorComponentwhich depends onUserRepositoryComponent. However, the authenticator component has an abstract valencryptionMethod, used to configure the encryption algorithm. How can I configure the component? There are two ways. The abstract val can be concretized when defining the env, e.g.:val env = new MysqlDatabaseComponentImpl with UserRepositoryComponent with UserAuthenticatorComponent { val encryptionMethod = EncryptionMethods.MD5 }But what if I want to re-use a configured component? An obvious answer is to extend the
UserAuthenticatorComponenttrait. However if that component has any dependencies (which, in the Cake Pattern, are expressed using self-types), they need to be repeated, as self-types are not inherited. So a reusable, configured component could look like this:trait UserAuthenticatorComponentWithMD5 extends UserAuthenticatorComponent { // dependency specification duplication! this: UserRepositoryComponent => val encryptionMethod = EncryptionMethods.MD5 }If we don’t repeat the self-types, the compiler will complain about incorrect
UserAuthenticatorComponentusage.No control over initialization order
A problem also related to configuration, is that there is no type-safe way to assure that the components are initialized in the proper order. Suppose as above that the
UserAuthenticatorComponenthas an abstractencryptionMethodwhich must be specified when creating the component. If we have another component that depends onUserAuthenticatorComponent:trait PasswordEncoderComponent { this: UserAuthenticatorComponent => // encryptionMethod comes from UserAuthenticatorComponent val encryptionAlgorithm = Encryption.getAlgorithm(encryptionMethod) }and initialize our system as follow:
val env = new MysqlDatabaseComponentImpl with UserRepositoryComponent with UserAuthenticatorComponent with PasswordEncoderComponent { val encryptionMethod = EncryptionMethods.MD5 }then at the moment of initialization of
encryptionAlgorithm,encryptionMethodwill benull! The only way to prevent this is to mix in theUserAuthenticatorComponentWithMD5before thePasswordEncoderComponent. But the type checker won’t tell us that.Pros
Don’t get me wrong that I don’t like the Cake Pattern – I think it offers a very nice way to structure your programs. For example it eliminates the need for factories (which I’m not a very big fan of), or nicely separates dependencies on components and dependencies on data (*). But still, it could be better ;).
(*) Here each code fragment has in fact two types of arguments: normal method arguments, which can be used to pass data, and component arguments, expressed as the self type of the containing component. Whether these two types of arguments should be treated differently is a good question :).
What are your experiences with DI in Scala? Do you use a Java DI framework, one of the approaches used above or some other way?
Adam
27 responses to “DI in Scala: Cake Pattern pros & cons”

-
Hi,
For the initialization issue, you can (and probably should) use lazy vals. It’s a less error-prone approach.
Regarding the repetition of dependencies issue, it can be worse than you show if there are multiple dependencies. In that case, you can use a type alias (using Scala’s type keyword) to make it less painful.
Best,
Ismael -
Regarding the “Parametrizing the system with a component implementation” issue.
You can also override the specified implementation at a later point.
E.g.:
trait DefaultEnvironment extends DatabaseComponent with UserRepositoryComponent with UserAuthenticatorComponent {
lazy val database = new MysqlDatabaseImpl
lazy val userRepository = new UserRepositoryImpl
lazy val userAuthenticator = new UserAuthenticatorImpl
}val env1 = new DefaultEnvironment() { /* nothing to do here */ }
val env2 = new OtherStuff with DefaultEnvironmentNow if you want to allow to override a specific part at a later time you need to spefiy the abstract type once more so scalac will not set it to the this.type.
E.g.:
trait DefaultEnvironment extends DatabaseComponent with UserRepositoryComponent with UserAuthenticatorComponent {
lazy val database: Database = new MysqlDatabaseImpl//see type annotation
lazy val userRepository = new UserRepositoryImpl
lazy val userAuthenticator = new UserAuthenticatorImpl
}val env = new ProdEnv with DefaultEnvironment {
override lazy val database = new MockDatabaseImpl
//lazy val userAuthenticator = new UserAuthenticatorImpl//this would yield an error since type has not been specified again
} -
Eric Bowman April 29th, 2011 at 19:51
Doesn’t making everything lazy mean that reflection is later used to access it? Might be better to use object instead of lazy val?
-
Stephen April 30th, 2011 at 07:03
Personally, I’m not fond of either cake or traditional Spring/Guice DI approaches. As in this post and your object services post, I think you end up fighting them more often than they’re worth.
I like making a single interface (basically a parameter object) for all app-wide services and just passing it around:
http://www.draconianoverlord.com/2011/03/17/frameworkless-di.html
My post used Java for its examples, but I’ve done the same thing in Scala and it’s even nicer as you can use val/def/lazy val.
E.g.
trait Services {
def database: Database
}class ProdServices extends Services {
lazy val database = new RealDatabase
}class TestServices extends Services {
val database = new StubDatabase
}I won’t say it’s perfect, but it’s simple, which I like.
-
In our commercial project we’ve used lazy vals (without the Cake pattern) extensively, with good results.
I share your concern described in “Parametrizing the system with a component implementation”, as 20 dependencies mixed in using “with” keyword are clearly unmanageable and anti-DRY. But look, so would be 20 arguments of a constructor or 20 method parameters, wouldn’t they? The problem is not that you cannot parametrize the system, because you have already 20 knobs and levers for that. The problem is you have too many of them.
But software engineering has already been solving such problems – look at the method parameters analogy. Long parameter list often indicates, that some Parameter Object craves to be extracted. You can apply similar approach to mixins.
Now look for traits that (if put together) form a cohesive abstraction and create a trait that extends them all. If you manage to divide 20 traits into 4 cohesive entities, each aggregating 5 traits, you’ll end up with a system containing of 4 components instead of 20. That probably solves your problems, hopefully improving you design at the same time (it’s important to extract only meaningful, not-strictly-technical entities or troubles are to be expected).This proposal has its limits of course, but probably is worth considering when you stick to the Cake pattern.
-
Yes @Adam, that’s not strictly the same but indeed very similar. Instead of writing:
def setup(db: Database) = setup(db, userRepository, userAuthenticator, …)
// and then
val system = setup(myConcreteDb)you can just as easy write:
trait Env extends UserRepository with UserAuthenticator with … { this: Database =>
}
// and then
object system extends Env with MyConcreteDbwith similar syntactic noise level and nearly the same result.
In fact, the innocently-looking one-arg setup method actually contains a specific configuration of components in its implementation. Perhaps that configuration deserves to be named in some meaningful way and communicated outside. In such case, defining a trait has its merits.
-
Kristian Domagala May 3rd, 2011 at 03:52
I also found that the Cake Pattern doesn’t work well with non-singleton-type dependencies: http://stackoverflow.com/questions/5190328/can-the-cake-pattern-be-used-for-non-singleton-style-dependencies
-
@Adam I’m not sure if I understand your proposal, but you definitely shouldn’t “new” in constructor/field initializers (with exception of simple value objects). Object dependencies should be minimal, with profits for testability and design. Kristian’s design is OK with respect to that rule (except for HttpServiceImpl, but desire to keep the example clear could be the reason for that – is that guess correct @Kristian?).
In short, Cake does not support multiple dependencies of one type. My advise would be to mix it with the plain-old constructor approach in such cases.
-
@Adam Essentially what you’ve proposed is HttpServiceComponent to become a Factory (in a sense of http://en.wikipedia.org/wiki/Factory_method_pattern), optionally equipped with caching. Nothing related particularly to Cake here, as Cake lets you manage various components, including Factories. Only HttpServiceComponent’s name should start to clearly communicate its purpose then – what about “HttpServiceFactoryComponent”?
While in some cases it makes sense to inject a Factory to another component (so it could obtain references to new collaborators) in most cases it does more harm, than good.For example, consider what would happen, if Kristian would refactor his code according to your advice. Two nice and simple classes: CompanyServiceImpl and TradeServiceImpl now would need to know 3 new things:
1. What is HttpServiceFactoryComponent
2. How do you get HttpService from it
3. What String to use to obtain correct HttpService instanceMore of almost identical code in those classes, more to worry.
Should it really be responsibility of those two simple and nice classes? I think not at all. They should stay unpolluted with that concerns, remaining easier to test, less problematic with refactoring, more decoupled, basically designed better.
Regarding your comment’s last sentence it’s not entirely clear to me what you’ve meant, but obviously the less assumptions components make, the less chances for bugs occur.
-
…and to conclude my last comment (from 19:04):
1. Having non-singleton-type dependencies is better than having factories injected to the components in the majority of cases.
2. Cake pattern doesn’t support this and we have to live with that.
3. The best workaround to that is to mix the Cake with usage of plain-old constructors, like in my StackOverflow answer to Kristian’s question. -
Stephen May 7th, 2011 at 05:10
Adam:
> when writing tests–it is not so obvious which components should be mocked
True. Two thoughts:
1) People generally don’t worry about whether a servlet depends on a request’s parameters or a request’s headers–it just depends on the request.
Testing would be easier if you knew exactly which parts of the request a servlet needed, but it’s generally accepted that the price of not knowing this is worth the benefit (simplicity, shorter parameter lists) of the Request abstraction.
Same thing–I don’t worry which specific app-wide dependencies an object needs; it needs an Application. That’s good enough.
2) Being ambivalent about which app-wide dependencies an object needs generally works best if you use stubs instead of mocks.
Think of Spring’s misnamed MockHttpServletRequest, which uses state to make it easy to test servlets (you don’t need to re-mock every single detailed operation of the request–the dummy implementation is good enough).
Same thing–I create one StubApplication class that each test instantiates. The StubApplication has stub versions of all the app’s dependencies, so it really doesn’t matter which dependencies the object under test does/does not need.
This has the added benefit of keeping my tests clean of redundant mock setup code that, IMO, is not very dry when a dependency is used by lots of tests, but each one dedicates several LOC to mocking out its behavior. A stub can usually capture that behavior in just one place.
-
A short clarification on one of the issues raised on the side in previous comments:
Scala does *not* use reflection for lazy vals. It uses the (slightly modified) double-checked initialization pattern in their accessor method. -
Sure @Adam, I can imagine, that factories provide great benefits in case of rich wrappers you’ve mentioned and I’m sure there is a lot more cases when they make a perfect fit. And I fully share your point of view about the common usage of DI frameworks, that leads to proliferation of singletons – a horrible pathology! Still in most cases I would use one factory per one object lifecycle/scope (like application, session, request), probably splitting them as they gain more responsibilities. The blog of Miško Hevery has opened my eyes with respect to that – I really recommend his posts.
Whether the “Factory” should be a part of the component’s name or not is actually a minor concern for me. What’s important, the purpose of the component should be clearly communicated. Should it happen through its name or in some other way (like by a convention), this is a secondary issue to me, as long as things are kept consistent.
Here I will go to “the beef”. I’m glad you’ve realised, that in the Kristian’s example injecting just a HttpService might be the proper solution. I would treat the address as a configuration parameter too. Only I’m not sure I understand your remark, that the address could be an abstract val. Because you need two HttpServices (each configured differently), you probably need two such vals.
Addressing your remarks to my points. They would be correct if you could assume lack of further changes to the software. However the “soft” in software lets you expect, that you’ll be able to easily modify it. When you let TradeServiceComp know about HttpServiceFactoryComp, you basically increase its coupling. In practice, every change to the factory interface forces you to update TradeServiceComp and other coupled components. The 3 points I’ve listed relate to design decisions, that once changed, cause a kind of ripple effect through the factory’s clients.
Now the critical question: should we care? What’s the probability the factory’s interface changes? I don’t know, I’m not really good at guessing the future. But I often witness the Murphy’s Law in action = when things can go wrong, they will. And I can imagine scenarios where that kind of coupling does hurt.Say my application was a success, it grew bigger and was split into several modules, each one handed out to a different team. Then it turned out, that misconfiguration of HTTP URLs was a common source of hard-to-diagnose customer problems. Besides taking other steps, I’ve decided to replace the type of HttpServiceFactoryComp method’s parameter from String to UrlPrefix – a value object, that validates the given URL and provides helpful messages in case of errors. So get down to code. First, I’d better check out all of the modules, because that change will affect most of them. Second, I do the change and fix the compilation errors. Now I run the tests – obviously some of them fail, because factory’s mocks expected a String and not a UrlPrefix. I fix the tests. Alright, this is it, right? Actually not – I wasn’t aware, that some team was working on one of modules I’ve changed – and they were faster with their check-in to the source control. When I synchronized with the repository a lot of conflicts came out. Happens, doesn’t it? I fix them – guys just have renamed a lot of their classes, so I move my changes to each of them. I’m ready now, I run the tests and check-in the code. The guys from yet another team, that are in the course of other refactoring will take care of their conflicts for themselves, right? Ah wait, our other source control branches have remained. According to our “propagate early and often” policy I should also put my changes to several other branches as well. So, a small switch in source control and here we go…
Sounds unrealistic? Maybe, but sadly I’ve already experienced similar situations. People waste a lot of time for dealing with consequences of excessive coupling, that could have been minimized beforehand.Regarding your last proposal, it doesn’t solve the coupling problem, which is only moved to the new intermediary components. You could argue, that the coupling always has to be present in one or another place. That’s true, but given the choice: to have it in several modules or in one application-wide bootstrap factory, I would certainly prefer the latter. Then, should the HttpServiceFactoryComp interface change, only bootstrap factory needs changing.
Summing up, factories make for great design choices sometimes, but you should always take care to use them properly – otherwise you may end up with troubles you haven’t expected. Introducing unnecessary coupling is an example of how you can make your life harder for yourself.
The point of Kristian (that in Cake you cannot mix a component twice) still holds, although in practice you can work this around.
I really recommend blog of Miško Hevery – it offers a lot of great advice on OO design.P.S. Unfortunately I wasn’t able to visit GeeCON this year. But will gladly meet you by another occasion :)
-
Now I’ve grasped your point about that intermediary component, I think. Having a HttpServiceComponent acting as a factory, but using it in the very place of final component assembly is definitely the right way to go, so coupling is minimized.
Only then you don’t need to declare self-type of TSHDSComponent:trait TradeServiceHttpDataSourceComponent {
this: HttpServiceComponent =>(I guess that it was just an oversight) – and the abstract vals are actually HttpServices and not mere Strings (that is evident in your code sample, but was not clear to me from the preceding description).
Regarding Misko articles, the majority of them deserve to be known to even experienced OO developers, really. In this case, when I was talking about “one factory per one object lifecycle” I had http://misko.hevery.com/2008/09/10/where-have-all-the-new-operators-gone/ in mind. If you think of it, it quickly becomes evident, but even developers skilled in GoF design patterns happen to miss the basic rules in their daily practice, thus making their lives harder. Should more developers apply the basic principles wonderfully explained by Misko, the world would be a better place for all of us. Until then, never too much reminding about the basics :)
Thanks for the discussion, too!
-
Hi!
You could be interested by my IoC container project called “Sindi”:
http://aloiscochard.github.com/sindiCurrently in alpha stage but you can find a working example here:
https://github.com/aloiscochard/sindi/tree/master/sindi-examples/demoBest regards,
Alois Cochard
1 Trackbacks / Pingbacks
-
Introducting Diesel – PHP Dependency Injection | The Box Developer Blog February 16th, 2012 at 02:54
[...] manner. Diesel is pure PHP and does not rely on attributes or XML files. It’s not quite a cake pattern – PHP does not provide mix-in capabilities — but does provide a similar concept of [...]
Leave a reply
-











Twitter
Ismael Juma April 29th, 2011 at 09:43