11

私は Scala と Play を初めて使用します。そして、ビジネス ロジックとプレゼンテーション ロジックの両方を含む "do all" コントローラーを作成しました。コントローラーからビジネス ロジックをリファクタリングしたいと考えています。

これが私の Scala/Play の外観です。きれいなインターフェースを使用して、このコントローラーからビジネスロジックをリファクタリングするための良い/慣用的な方法は何ですか?

object NodeRender extends Controller {
...
def deleteNode(nodeId: Long) = Action { request =>
    //business logic
    val commitDocument = Json.toJson(
    Map(
        "delete" -> Seq( Map( "id" -> toJson( nodeId)))  
    ))
    val commitSend   = Json.stringify( commitDocument)
    val commitParams = Map( "commit" -> "true", "wt" -> "json")
    val headers = Map( "Content-type" -> "application/json")

    val sol = host( "127.0.0.1", 8080)
    val updateReq  = sol / "solr-store" / "collection1" / "update" / "json" <<?
        commitParams <:< headers << commitSend

    val commitResponse = Http( updateReq)()

    //presentation logic
    Redirect( routes.NodeRender.listNodes)
}

Python/Django では、2 つのクラスを作成XApiHandlerXBackend、それらの間でクリーンなインターフェイスを使用しています。

xb = XBackend( user).do_stuff()
if not xb:
  return a_404_error
else:
  return the_right_stuff( xb.content) #please dont assume its a view!
4

3 に答える 3

7

いくつかの仮定:

1) 最後から 2 行目の HTTP 呼び出しがブロックされます

2) リダイレクトが Http 呼び出しからの応答を待つ必要があるかどうかはわかりませんが、そうすると思います。

リクエストを処理するスレッドをブロックしないように、ブロッキング呼び出しを別のスレッドに移動する必要があります。Play ドキュメントはこれについて非常に具体的です。Akka.futureと組み合わせた機能がAsync役立ちます。

コントローラーコード:

1 def deleteNode(nodeId: Long) = Action { request =>
2     Async{
3         val response = Akka.future( BusinessService.businessLogic(nodeId) )
4 
5         response.map { result =>
6             result map {
7                 Redirect( routes.NodeRender.listNodes)
8             } recover {
9                 InternalServerError("Failed due to ...")
10            } get 
11        }
12    }
13}

これは PHP よりも少し多くなりますが、マルチスレッドです。

3 行目に渡されたコードAkka.futureは、将来別のスレッドを使用して呼び出されます。ただし、 への呼び出しAkka.futureはすぐに a を返しますFuture[Try](ビジネス メソッドの戻り値の型については、以下を参照してください)。つまり、変数responseの型はFuture[Try]. 5 行目のメソッドへのmap呼び出しは、マップ ブロック内のコードを呼び出すのではなく、そのコード (6 ~ 10 行目) をコールバックとして登録します。スレッドは 5 行目でブロックせずFuture、ブロックに戻りますAsyncAsyncブロックは Play に a を返し、それAsyncResultは Play に Future が完了したときにコールバックのために自分自身を登録するように指示します。

その間、他のスレッドがBusinessService3 行目から呼び出しを行い、バックエンド システムに対して行った HTTP 呼び出しが返されると、response3 行目の変数は「完了」します。つまり、6 ~ 10 行目のコールバックが完了します。呼ばれます。 には抽象result型があり、サブクラスはとの 2 つだけです。が成功した場合、メソッドは 7 行目を呼び出し、それを new でラップします。が失敗した場合、map メソッドは失敗を返します。8行目のメソッドは反対のことをしています。map メソッドの結果が成功の場合は、成功を返します。それ以外の場合は、9 行目を呼び出して( ! ではなく) でラップします。への呼び出しTrySuccessFailureresultmapSuccessresultrecoverSuccessFailureget10 行目のメソッドは、からリダイレクトまたはエラーを取得しSuccess、その値を使用しAsyncResultて Play が保持している を完成させます。次に Play は、応答の準備ができており、レンダリングして送信できるというコールバックを取得します。

このソリューションを使用すると、着信要求を処理するスレッドがブロックされなくなります。たとえば 4 コアのマシンでは、Play には受信リクエストを処理できるスレッドが 8 つしかないため、これは重要です。少なくともデフォルト構成を使用している場合は、新しいものは生成されません。

ビジネス サービス オブジェクトのコードは次のとおりです (コードのほとんどをコピーしたものです)。

def businessLogic(nodeId: Long): Future[Try] {

    val commitDocument = Json.toJson(
    Map(
       "delete" -> Seq( Map( "id" -> toJson( nodeId)))  
    ))
    val commitSend   = Json.stringify( commitDocument)
    val commitParams = Map( "commit" -> "true", "wt" -> "json")
    val headers = Map( "Content-type" -> "application/json")

    val sol = host( "127.0.0.1", 8080)
    val updateReq  = sol / "solr-store" / "collection1" / "update" / "json" <<?
        commitParams <:< headers << commitSend

    val commitResponse = Http( updateReq)()

    Success(commitResponse) //return the response or null, doesnt really matter so long as its wrapped in a successful Try 
}

プレゼンテーション ロジックとビジネス ロジックは完全に切り離されています。

詳細については、 https://speakerdeck.com/heathermiller/futures-and-promises-in-scala-2-dot-10およびhttp://docs.scala-lang.org/overviews/core/futures.htmlを参照してください。

于 2013-03-09T21:25:39.327 に答える
1

私は専門家ではありませんが、一貫したロジック ブロックをミックスイン トレイトに分解することにかなり満足しています。

abstract class CommonBase {
    def deleteNode(): Unit
}


trait Logic extends CommonBase{
  this: NodeRender =>

  override def deleteNode(): Unit = {
    println("Logic Here")
    println(CoolString)
    }
}

class NodeRender extends CommonBase
    with Logic
{
    val CoolString = "Hello World"

}



object test {
    def main(args: Array[String]) {
      println("starting ...")
      (new NodeRender()).deleteNode()
    }
}

版画

starting ...
Logic Here
Hello World
于 2013-03-07T17:10:02.657 に答える