Play-Slick:是否可以改进这种设计(图案)...以及如何调用? [英] Play-Slick: Is it possible to improve this design (pattern) ... and how to call it?
问题描述
这是初始设置:
- 生成的Slick模型在
generated.Tables ._
- Generic Slick dao实现
- 建立在Generic Slick dao之上的服务层
因为它允许将服务层功能插入到模型中,所以临时称为可插拔服务的模式背后的力量:
- Play的控制器和视图只能看到服务层(而不是Dao的)
UserService
- 生成的模型例如
UserRow
预计将符合业务层接口,例如Deadbolt-2的主题,但不直接实现。为了能够实现它,需要太多,例如UserRow
模型类型,UserDao
和潜在的一些业务环境。 - 一些
UserService
方法自然适用于模型UserRow
instance egloggedUser.roles
或loggedUser.changePassword
因此我有:
generated.Tables.scala
Slick模型类:
case class UserRow(id:Long,username:String,firstName:String,
lastName: String,...)扩展EntityAutoInc [Long,UserRow]
dao.UserDao.scala
特定于用户模型的道扩展和自定义:
@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){
//隐式执行一个DBIO,并无限期地等待
//未来完成
import utils.DbExecutionUtils._
// ------------------------------------- -----------------------------------
// Deadbolt-2主题实现需要一个List [角色] 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 {
覆盖def角色:列表[角色]
}
对象PluggableUserService {
隐式类toPluggable(user:UserRow)(隐式userService:UserService)
扩展PluggableUserService {
// ----------------------------------------- -------------------------------
override def roles:List [Role] = {
userService .roles(user)
}
}
最后可以在控制器:
@Singleton
class Application @Inject()(implicit
val messagesApi:MessagesApi,
会话:会话,
deadbolt:DeadboltActions,
userService:UserService)扩展控制器与I18nSupport {
import services.PluggableUserService._
def index = deadbolt.WithAuthRequest()(){implicit request =>
未来{
val用户:UserRow = userService.findUserInSession(会话)
//自动魔术地将服务插入模型
val roles = user.roles
// ...
Ok(views.html.index)
}
}
有没有Scala方式可以帮助不必在可插拔服务对象中编写样板代码?可插拔服务名称是否有意义?
常见变体之一可能是您的控制器的父属特征,这些行:
def MyAction [A](bodyParser:BodyParser [A] = parse.anyContent)
:(UserWithRoles)=>(AuthenticatedRequest [A])=> Future [Result]):Action [A] = {
deadbolt.WithAuthRequest()(bodyParser){request =>
val user:UserRow = userService.findUserInSession(session)
//这可能是你原来的
//但是我没有看到没有理由不
/ /直接从db或
//中明确地将其与角色一起放在会话中(如以下UserWithRoles类)
val roles = user.roles
块(UserWithRoles(用户,角色))(请求)
}
这里的房间是你如何获得 userService
实例。那么你需要在你的控制器构造函数中显式地要求它(与 DeadboltActions
一样)。或者,您可以将 DeadboltActions
, UserService
以及其他类别(例如 ControllerContext
?),并将这个单个实例注入一个构造函数参数(但这可能是另一个讨论...)。
之后,你的控制器代码就像这个:
def index = MyAction(){implicit user =>隐式请求=>
未来{
// ...
Ok(views.html.index)
}
}
用户
和请求
进入你的应用程序的内部部分(通常情况下 - 你带来用户
对象来执行一些业务逻辑)。
它不会摆脱您的 PluggableUserService
本身(逻辑仍然存在),但它可能会帮助您在您的控制器中随处可以更轻松地重复使用相同的逻辑(根据我的经验,您需要同时使用用户
与角色
更常见于任何真正的应用程序)。
编辑:我有一种感觉,我没有得到你的问题。您想避免在 PluggableUserService
中的样板,或者您希望避免在每个控制器中使用 PluggableUserService
分散此转换(IMHO第二选项是要避免的)?
I'm using Play-Slick versions 2.5.x and 3.1.x respectively. I use Slick's code generator and produce the Slick model from an existing database. Actually I'm shy to admit that I'm DB-design driven and not class-design driven.
This is the initial setup:
- Generated Slick model under
generated.Tables._
- Generic Slick dao implementation
- Service layer that builds on top of the Generic Slick dao
These are the forces behind the pattern which I temporary called "Pluggable Service" because it allows plugging in the service layer functionality to the model:
- Play's controllers and views must only see the Service layer (and not the Dao's) e.g.
UserService
- Generated model e.g.
UserRow
is expected to comply to business layer interfaces e.g. Deadbolt-2's Subject but not implement it directly. To be able to implement it one needs "too much" e.g. theUserRow
model type, theUserDao
and potentially some business context. - Some of the
UserService
methods naturally apply to the modelUserRow
instance e.g.loggedUser.roles
orloggedUser.changePassword
Therefore I have:
generated.Tables.scala
Slick model classes:
case class UserRow(id: Long, username: String, firstName: String,
lastName : String, ...) extends EntityAutoInc[Long, UserRow]
dao.UserDao.scala
Dao extensions and customizations specific to the User model:
@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
service that facades all user operations to the rest of the Play application:
@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
finally the actual "Pluggable" pattern that dynamically attaches service implementations to the model type:
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)
}
}
Finally one can do in the controllers:
@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)
}
}
Is there any Scala way that could help not having to write the boilerplate code in the Pluggable Service object? does the Pluggable Service name makes sense?
One of the common variant may be a parent trait for your controllers that has something along these lines:
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)
}
The elephant in the room here is how you get userService
instance. Well you would need to explicitly require it in your controller constructor (in the same way you do with DeadboltActions
). Alternatively you may bundle DeadboltActions
, UserService
and what else into one class (e.g. ControllerContext
?) and inject this single instance as one constructor parameter (but that's probably another discussion...).
After that your controller code would be like this:
def index = MyAction() { implicit user => implicit request =>
Future {
// ...
Ok(views.html.index)
}
}
both user
and request
is implicit which helps to pass into into inner parts of your application (which is often the case - you bring user
object to perform some business logic).
It doesn't get rid of your PluggableUserService
per se (logic is still there) but it may help you to easier reuse same logic everywhere in your controllers (as in my experience, you need to have both User
together with Roles
more often than not in any real application).
EDIT: I got a feeling I didn't quite get your question. You want to avoid boilerplate in PluggableUserService
or you want to avoid scattering this conversion with use of PluggableUserService
everywhere, in every controller (IMHO 2nd option is something to be avoided)?
这篇关于Play-Slick:是否可以改进这种设计(图案)...以及如何调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!