この質問の長さはご容赦ください。
多くの場合、コードの1つのレイヤーでコンテキスト情報を作成し、その情報を他の場所で使用する必要があります。私は通常、暗黙のパラメータを使用していることに気付きます。
def foo(params)(implicit cx: MyContextType) = ...
implicit val context = makeContext()
foo(params)
これは機能しますが、暗黙のパラメーターを頻繁に渡す必要があり、介在する関数のレイアウト後にレイヤーのメソッドシグネチャを汚染します(それ自体は気にしない場合でも)。
def foo(params)(implicit cx: MyContextType) = ... bar() ...
def bar(params)(implicit cx: MyContextType) = ... qux() ...
def qux(params)(implicit cx: MyContextType) = ... ged() ...
def ged(params)(implicit cx: MyContextType) = ... mog() ...
def mog(params)(implicit cx: MyContextType) = cx.doStuff(params)
implicit val context = makeContext()
foo(params)
このアプローチは醜いと思いますが、1つの利点があります。それはタイプセーフです。mog
正しいタイプのコンテキストオブジェクトを受け取るか、コンパイルされないかは確実にわかっています。
何らかの形の「依存性注入」を使用して関連するコンテキストを見つけることができれば、混乱が緩和されます。引用符は、これがScalaで見られる通常の依存性注入パターンとは異なることを示すためにあります。
開始点foo
と終了点mog
は、システムの非常に異なるレベルに存在する場合があります。たとえばfoo
、ユーザーログインコントローラーであり、mog
SQLアクセスを実行している可能性があります。一度に多くのユーザーがログインしている可能性がありますが、SQLレイヤーのインスタンスは1つだけです。mog
異なるユーザーによって呼び出されるたびに、異なるコンテキストが必要になります。したがって、コンテキストを受信オブジェクトにベイクすることはできません。また、2つのレイヤーを何らかの方法でマージすることもできません(ケーキパターンなど)。また、GuiceやSpringのようなDI/IoCライブラリに依存したくありません。私はそれらが非常に重く、Scalaにはあまり適していないことを発見しました。
したがって、私が必要だと思うのはmog
、実行時に正しいコンテキストオブジェクトを取得できるものThreadLocal
で、スタックが含まれているようなものです。
def foo(params) = ...bar()...
def bar(params) = ...qux()...
def qux(params) = ...ged()...
def ged(params) = ...mog()...
def mog(params) = { val cx = retrieveContext(); cx.doStuff(params) }
val context = makeContext()
usingContext(context) { foo(params) }
しかし、非同期アクターがチェーンのどこかに関与するとすぐに、それは低下します。どのアクターライブラリを使用するかは関係ありません。コードが別のスレッドで実行されると、が失われThreadLocal
ます。
だから...私が見逃しているトリックはありますか?介在するメソッドシグネチャを汚染せず、コンテキストをレシーバーに静的に焼き付けず、タイプセーフな情報をScalaでコンテキスト的に渡す方法はありますか?