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
に自然に適用されます。UserRow
loggedUser.roles
loggedUser.changePassword
したがって、私は持っています:
generated.Tables.scala
洗練されたモデル クラス:
case class UserRow(id: Long, username: String, firstName: String,
lastName : String, ...) extends EntityAutoInc[Long, UserRow]
dao.UserDao.scala
User モデルに固有の 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 の名前は理にかなっていますか?