Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala Cake Pattern and Dependency Collisions

I'm trying to implement dependency injection in Scala with the Cake Pattern, but am running into dependency collisions. Since I could not find a detailed example with such dependencies, here's my problem:

Suppose we have the following trait (with 2 implementations):

trait HttpClient {
  def get(url: String)
}

class DefaultHttpClient1 extends HttpClient {
  def get(url: String) = ???
}

class DefaultHttpClient2 extends HttpClient {
  def get(url: String) = ???
}

And the following two cake pattern modules (which in this example are both APIs that depend on our HttpClient for their functionality):

trait FooApiModule {
  def httpClient: HttpClient        // dependency
  lazy val fooApi = new FooApi()    // providing the module's service

  class FooApi {
    def foo(url: String): String = {
      val res = httpClient.get(url)
      // ... something foo specific
      ???
    }
  }
}

and

trait BarApiModule {
  def httpClient: HttpClient        // dependency
  lazy val barApi = new BarApi()    // providing the module's service

  class BarApi {
    def bar(url: String): String = {
      val res = httpClient.get(url)
      // ... something bar specific
      ???
    }
  }
}

Now when creating the final app that uses both modules, we need to provide the httpClient dependency for both of the modules. But what if we want to provide a different implementation of it for each of the modules? Or simply provide different instances of the dependency configured differently (say with a different ExecutionContext for example)?

object MyApp extends FooApiModule with BarApiModule {
  // the same dependency supplied to both modules
  val httpClient = new DefaultHttpClient1()

  def run() = {
    val r1 = fooApi.foo("http://...")
    val r2 = barApi.bar("http://...")
    // ...
  }
}

We could name the dependencies differently in each module, prefixing them with the module name, but that would be cumbersome and inelegant, and also won't work if we don't have full control of the modules ourselves.

Any ideas? Am I misinterpreting the Cake Pattern?

like image 230
orrsella Avatar asked Dec 08 '13 22:12

orrsella


1 Answers

You get the pattern correctly and you've just discovered its important limitation. If two modules depend on some object (say HttpClient) and happen to declare it under the same name (like httpClient), the game is over - you won't configure them separately inside one Cake. Either have two Cakes, like Daniel advises or change modules' sources if you can (as Tomer Gabel is hinting).

Each of those solutions has its problems.

Having two Cakes (Daniel's advice) looks well as long they don't need some common dependencies.

Renaming some dependencies (provided it's possible) forces you to adjust all code that uses those.

Therefore some people (including me) prefer solutions immune to those problems, like using plain old constructors and avoid Cake altogether. If you measured it, they don't add much bloat to the code (Cake is already pretty verbose) and they're much more flexible.

like image 168
Przemek Pokrywka Avatar answered Oct 05 '22 05:10

Przemek Pokrywka