The cake pattern is a lie

I thought the cake pattern is dead. I thought we all realised what a terrible mistake it was. I thought that I would see it only in old code which I have to maintain occasionally.

I was so happy to see the voting board during Scalar 2018 conference. I was happy because nobody voted for cake pattern as their method of dependency injection.

The whiteboard voting results — Scalar 2018
The whiteboard voting results — Scalar 2018

Unfortunately, I was wrong. I was wrong because a few days ago I read an article written by someone who rediscovered cake pattern and tried to apply it in Swift. The article is almost a year old, so the post is not old enough to ignore it.

I thought I would never again need to explain why the cake pattern is, in fact, an anti-pattern. Let’s do it one more time. Hopefully, for the last time ;)

What is cake pattern?

Let’s look at one of the first blog posts about cake pattern that was also used as an example in the blog post above.

In the end we have code which looks like this:

object ApplicationLive {
  val userServiceComponent = new DefaultUserServiceComponent with UserRepositoryJPAComponent {
    val em = Persistence.createEntityManagerFactory("cake.pattern").
                         createEntityManager()
  }
  val userService = userServiceComponent.userService
}

What is wrong with that?

  • I can bypass the user service and call functions from UserRepositoryJPAComponent directly.

  • I can override one of the UserRepositoryComponent functions and change the behaviour of the whole component. For example, I can delete a user instead of persisting the user:

override def userUpdater = new UserUpdater {
  def save(user: User) { em.remove(user) }
}

Not a cake

I was told that this pattern is called a “cake” because it introduces layers in your code. Does it? I can access all functions of my dependencies and all functions of my transitive dependencies. Where are the layers?

It does not look like a cake to me.

It would be more appropriate to call it an “Eintopf pattern.” You take all your classes, mix them, and keep your fingers crossed.

What is so terrible about the Scala implementation of cake pattern?

First of all, a class should not become its dependencies. When you inject dependencies you are supposed to introduce a “has a” relationship between your classes. A dependency is something that a class is supposed to have.

When you use the “cake pattern” your dependency becomes a part of the class. Instead of creating a “has a” relationship, you have a “is a” relationship.

Interface segregation principle

Additionally, the “cake pattern” violates the interface segregation principle. Your class exposes public functions that are not related to its primary responsibility. In fact, such a mix of classes has more than one responsibility, so we may argue whether the single responsibility principle is violated as well.

Open-close principle

The most terrible consequence of cake pattern is the violation of the “open-close principle.” A class can override any public function of its dependencies. I was supposed to depend on those classes, not modify their behavior. The only thing stopping me from doing that is common sense.

Someone may say that the problems with “cake patterns” are caused by its misuse, not be the pattern itself. In my opinion, you cannot use this pattern correctly. It is useless. It does not solve any problems and create a lot of new ones.

You can achieve the goal (passing the dependencies) much easier, use the constructor and pass the dependency as a parameter.

I think that “cake pattern” was another thing created by programmers because they were bored. Something created to feel smarter. Something you can show to others and make them feel stupid for a while (until they learn your new, clever pattern).

It is great that people want to learn something from Scala and use it in their programming languages. Please don’t repeat our mistakes and don’t use that terrible pattern.

The cake is a lie ;)

Older post

Can we make it more generic?

What can we learn from a horrible mistake made by a programmer who wanted to make the code more generic?

Newer post

Precision vs. recall - explanation

How to understand the difference between precision and recall?