7

我目前正在烘烤我的第一个蛋糕图案,所以请多多包涵。

我拿了我的工作单体应用程序,并将其切割成功能层。剪切看起来很干净,但导致两个层依赖于隐式 ActorSystem。

我试图像这样解决这种依赖关系:

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

...同样适用于 LayerX

我的装配类看起来像:

class Assembly extends LayerA with LayerB with LayerX with ActorSystemProvider

其中 ActorSystemProvider 只是实例化了actor系统。

这不起作用,因为ActorSystem当依赖关系被解析并且 val 被实例化时不存在,从而导致 NPE。这看起来也很丑陋,我相信必须有一种更好/更简单的方法来处理它。

在使用蛋糕模式时,我应该如何处理层之间的共享隐式依赖关系,就像ActorSystem在这种情况下一样?

谢谢

4

2 回答 2

11

自类型不是构建结块架构的要求,实际上我仅在特征是层的组件的情况下使用自类型。因此,当我需要在范围内放置一些隐式(例如用于 Spray Client 的 ActorRefFactory)时,我只需在其中混合一个特征:

trait ActorSystemProvider {
  implicit def actorSystem: ActorSystem
}

在最低层(所谓的“世界末日”)我有以下代码结构:

trait ServiceStack
  extends SomeModule
     with SomeModule2
     with SomeModule3 
     with ActorSystemProvider 

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

这是一个过于简单的例子(如果你想要一个建立在蛋糕模式之上的真实系统的好例子,那么你绝对应该看看Precog系统,不同模块/层连接的例子),但你不能混合隐式 ActorSystem当你需要它的时候。

于 2014-01-30T18:37:25.720 回答
5

如果您可以懒惰地实例化 val 而不是急切地实例化 val,则可以将implicitActorSystem 设置为懒惰的 val 而不是 val。所以它只有在第一次访问时才会执行。我认为这应该解决NPE的问题。(@ViktorKlang FYI 发布的另一个鲜为人知的有趣事实:如果惰性 val 的初始化引发异常,它将尝试在下次访问时重新初始化 val。)

另一种方法是让每个需要执行上下文的方法都接受一个隐式的 executionContext,例如:

trait LayerA {
  def getUser(id: Int)(implicit ec: ExecutionContext) = {
    ...
  }
}
于 2014-01-30T18:26:02.453 回答