Cake pattern w/ akka: Providing implicit actorSystem to several layers
Asked Answered
L

2

7

I'm currently baking my first cake pattern, so please bear with me.

I took my working monolithic app and I cutted it into functional layers. The cut looks clean but resulted in two of the layers that depend on an implicit ActorSystem.

I tried to solve this dependency like this:

trait LayerA {
  this: ActorSystemProvider =>
  private implicit val implicitActorSystem = actorSystem
  import implicitActorSystem.dispatcher // implicit execution ctx
  ...
}

... and similarly for LayerX

My assembly class looks like:

class Assembly extends LayerA with LayerB with LayerX with ActorSystemProvider

where ActorSystemProvider simply instantiates the actor system.

This does not work given that the ActorSystem does not exist when the dependencies are resolved and the val's are instantiated, resulting in a NPE. This also looks really ugly and I'm sure there has to be a nicer/easier way to deal with it.

How should I deal with shared implicit dependencies among layers when using the cake pattern, like ActorSystem in this case?

Thanks

Lachus answered 30/1, 2014 at 18:7 Comment(0)
A
11

Self types is not a requirement for building a caked architecture, actually i use self types only in cases when a trait is a component of a layer. So when i need to place some implicit into the scope (for example ActorRefFactory for Spray Client) i just mix a trait in :

trait ActorSystemProvider {
  implicit def actorSystem: ActorSystem
}

And on the lowest layer (so called "end of the world") i have the following code structure:

trait ServiceStack
  extends SomeModule
     with SomeModule2
     with SomeModule3 
     with ActorSystemProvider 

object ServiceLauncher extends App with ServiceStack {
  val actorSystem = ActorSystem("ServiceName")
}

It's an oversimplified example (if you want a great example of a real system build on top of a Cake Pattern then you should definitely take a look at the Precog system, example where different modules/layers connects), but not you can mix implicit ActorSystem when you need it.

Ancillary answered 30/1, 2014 at 18:37 Comment(2)
@Alexv I don't understand where you reference ActorSystemProvider - how do I access then the actorSystem from an internal trait like myInternalCalculator it is used deep in the call hierarchy.Equerry
@Equerry It looks like it should be with ActorSystemProvider instead of ActorRefProvider. Then you can init it in something similar to ServiceLauncherAncillary
U
5

If you can instantiate the vals lazily rather than eagerly, you can make the implicitActorSystem a lazy val instead of a val. So it only gets executed when it is accessed the first time. I think this should solve the problem of NPE. (Another little known interesting fact posted by @ViktorKlang FYI: If the initialization of a lazy val throws an exception, it will attempt to reinitialize the val at next access.)

Another way would be to make each of your methods which need execution context accept an implicit executionContext like:

trait LayerA {
  def getUser(id: Int)(implicit ec: ExecutionContext) = {
    ...
  }
}
Uninstructed answered 30/1, 2014 at 18:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.