0

Play-Slick バージョン 2.5.x と 3.1.x をそれぞれ使用しています。Slick のコード ジェネレーターを使用して、既存のデータベースから Slick モデルを生成します。実際、私は DB 設計主導であり、クラス設計主導ではないことを認めるのは恥ずかしがり屋です。

これは初期設定です:

  • 下に生成された Slick モデルgenerated.Tables._
  • ジェネリック Slick dao 実装
  • Generic Slick dao の上に構築されるサービス層

これらは、サービス層の機能をモデルにプラグインできるようにするため、一時的に「プラグ可能なサービス」と呼んだパターンの背後にある力です。

  • Play のコントローラーとビューは、(Dao ではなく) Service レイヤーのみを参照する必要があります。UserService
  • 生成されたモデルなどUserRowは、Deadbolt-2 のサブジェクトなどのビジネス層インターフェイスに準拠することが期待されますが、直接実装することはできません。UserRowそれを実装できるようにするには、モデル タイプ、UserDaoビジネス コンテキストなど、 「多すぎる」ものが必要です。
  • 一部のメソッドは、モデルインスタンスUserServiceに自然に適用されます。UserRowloggedUser.rolesloggedUser.changePassword

したがって、私は持っています:

generated.Tables.scala洗練されたモデル クラス:

case class UserRow(id: Long, username: String, firstName: String, 
                   lastName : String, ...) extends EntityAutoInc[Long, UserRow]

dao.UserDao.scalaUser モデルに固有の Dao 拡張機能とカスタマイズ:

@Singleton
class UserDao @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)
    extends GenericDaoAutoIncImpl[User, UserRow, Long] (dbConfigProvider, User) {
  //------------------------------------------------------------------------
  def roles(user: UserRow) : Future[Seq[Role]] = {
    val action = (for {
      role <- SecurityRole
      userRole <- UserSecurityRole if role.id === userRole.securityRoleId
      user <- User if userRole.userId === user.id
    } yield role).result

    db.run(action)
  }
}

services.UserService.scalaすべてのユーザー操作を Play アプリケーションの残りの部分にファサードするサービス:

@Singleton
class UserService @Inject()(auth : PlayAuthenticate, userDao: UserDao) {
  // implicitly executes a DBIO and waits indefinitely for 
  // the Future to complete
  import utils.DbExecutionUtils._
  //------------------------------------------------------------------------
  // Deadbolt-2 Subject implementation expects a List[Role] type 
  def roles(user: UserRow) : List[Role] = {
    val roles = userDao.roles(user)
    roles.toList
  }
}

services.PluggableUserService.scala最後に、サービス実装をモデル タイプに動的にアタッチする実際の「プラガブル」パターン:

trait PluggableUserService extends be.objectify.deadbolt.scala.models.Subject {
  override def roles: List[Role]
}

object PluggableUserService {
  implicit class toPluggable(user: UserRow)(implicit userService: UserService) 
    extends PluggableUserService {
    //------------------------------------------------------------------------
    override def roles: List[Role] = {
      userService.roles(user)
    }
}

最後に、コントローラーで次のことができます。

@Singleton
class Application @Inject() (implicit
                             val messagesApi: MessagesApi,
                             session: Session,
                             deadbolt: DeadboltActions,
                             userService: UserService) extends Controller with I18nSupport {
  import services.PluggableUserService._                           

  def index = deadbolt.WithAuthRequest()() { implicit request =>
    Future {
      val user: UserRow = userService.findUserInSession(session)
      // auto-magically plugs the service to the model
      val roles = user.roles 
      // ...
      Ok(views.html.index)
    }
  }                                

Pluggable Service オブジェクトに定型コードを書かなくても済むようにする Scala の方法はありますか? Pluggable Service の名前は理にかなっていますか?

4

1 に答える 1

1

一般的なバリアントの 1 つは、次の行に沿った何かを持つコントローラーの親トレイトである可能性があります。

def MyAction[A](bodyParser: BodyParser[A] = parse.anyContent)
               (block: (UserWithRoles) => (AuthenticatedRequest[A]) => Future[Result]): Action[A] = {
    deadbolt.WithAuthRequest()(bodyParser) { request =>
    val user: UserRow = userService.findUserInSession(session)
    // this may be as you had it originally
    // but I don't see a reason not to 
    // simply pull it explicitly from db or 
    // to have it in the session together with roles in the first place (as below UserWithRoles class)
    val roles = user.roles 

    block(UserWithRoles(user, roles))(request)
}  

ここの部屋の象は、userServiceインスタンスを取得する方法です。コントローラーコンストラクターで明示的にrequireする必要があります( と同じ方法でDeadboltActions)。DeadboltActionsまたは、UserServiceとその他のものを 1 つのクラス (たとえば?) にバンドルし、ControllerContextこの単一のインスタンスを 1 つのコンストラクター パラメーターとして注入することもできます (ただし、それはおそらく別の議論です...)。

その後、コントローラーのコードは次のようになります。

def index = MyAction() { implicit user => implicit request =>
    Future {
      // ...
      Ok(views.html.index)
    }
  } 

両方ともuser暗黙request的であり、アプリケーションの内部部分に渡すのに役立ちます (多くの場合、userオブジェクトを持ってきてビジネスロジックを実行します)。

それはあなた自身を取り除くことはありませんPluggableUserService(ロジックはまだそこにあります)が、コントローラーのどこでも同じロジックを簡単に再利用するのに役立つかもしれません(私の経験では、実際には両方Userを一緒にする必要がありますRoles応用)。

編集:あなたの質問がよくわからなかったと感じました。定型文を避けたいですか、それともすべてのコントローラーでどこでもPluggableUserService使用してこの変換を分散させたくないですか(IMHO 2番目のオプションは避けるべきものです)?PluggableUserService

于 2016-12-16T23:36:05.553 に答える