2016-12-16 1 views
0

J'utilise les versions Play-Slick 2.5.x et 3.1.x respectivement. J'utilise le générateur de code de Slick et produis le modèle Slick à partir d'une base de données existante. En fait, je suis timide d'admettre que je suis axé sur la conception DB et non sur la conception de classe.Play-Slick: Est-il possible d'améliorer cette conception (modèle) ... et comment l'appeler?

Ceci est la configuration initiale:

  • modèle généré Slick sous generated.Tables._
  • application générique Slick dao
  • couche de service qui construit sur le dessus des marées noires Générique dao

Ce sont les forces derrière le modèle que j'ai temporairement appelé "Pluggable Service" parce qu'il permet de brancher la fonctionnalité de la couche de service au modèle:

  • Les contrôleurs de lecture et les vues doivent uniquement voir la couche Service (et non les Dao), par ex. UserService
  • Modèle généré par ex. UserRow devrait être conforme aux interfaces de couche de gestion, par ex. Deadbolt-2's Subject mais ne l'implémente pas directement. Pour pouvoir l'implémenter, il faut "trop", par ex. le type de modèle UserRow, le UserDao et éventuellement un contexte métier.
  • Certaines des méthodes UserService s'appliquent naturellement à l'instance modèle UserRow, par ex. loggedUser.roles ou loggedUser.changePassword

pourquoi je:

generated.Tables.scala classes de modèle Slick:

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

dao.UserDao.scala extensions Dao et personnalisations spécifiques au modèle de l'utilisateur:

@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 façades toutes les opérations de l'utilisateur au reste de l'application 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 enfin le modèle "Pluggable" réelle qui se fixe de façon dynamique les implémentations de service au type de modèle:

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) 
    } 
} 

Enfin on peut faire dans les contrôleurs:

@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) 
    } 
    }         

Existe-t-il un moyen Scala qui pourrait aider à ne pas avoir à écrire le code standard dans l'objet Service Pluggable? le nom du service Pluggable est-il logique?

+2

Cette question appartient à http://codereview.stackexchange.com/ – Odomontois

+0

Il s'agit d'une question de conception, le code est à titre d'exemple ... en plus de l'activité Scala de codereview est proche de inexistant. –

+0

Il me manque une chose ici: pourquoi 'findUserInSession' ne peut pas récupérer' User' avec 'roles' - quelque chose comme' UserWithRoles (user: UserRow, roles: List [Rôle]) '? –

Répondre

1

Une de la variante commune peut être un trait de parent pour vos contrôleurs qui a quelque chose le long de ces lignes:

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) 
} 

L'éléphant dans la pièce voici comment vous userService exemple. Eh bien, vous auriez besoin de l'exiger explicitement dans votre constructeur de contrôleur (de la même manière que vous le faites avec DeadboltActions). Vous pouvez également regrouper DeadboltActions, UserService et quoi d'autre dans une classe (par exemple, ControllerContext?) Et injecter cette instance unique en tant que paramètre constructeur (mais c'est probablement une autre discussion ...).

Après que votre code de contrôleur serait comme ceci:

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

à la fois user et request est implicite qui aide à passer dans dans les parties internes de votre application (ce qui est souvent le cas - vous apporter user objet effectuer une certaine logique métier).

Il ne se débarrasse pas de votre PluggableUserService en tant que tel (la logique est toujours là), mais il peut vous aider à réutiliser facilement la même logique partout dans vos contrôleurs (comme dans mon expérience, vous devez avoir à la fois User avec Roles plus souvent qu'autrement dans toute application réelle).

EDIT: J'ai l'impression que je n'ai pas bien compris votre question. Vous voulez éviter d'utiliser la norme dans PluggableUserService ou vous voulez éviter de disperser cette conversion avec l'utilisation de PluggableUserService partout, dans chaque contrôleur (IMHO 2ème option est quelque chose à éviter)?

+1

N'oubliez pas, vous pouvez obtenir l'utilisateur en tant qu'option à partir de la requête authentifiée. –

+0

@ (Steve Chaloner) sympa! bon à savoir! –

+0

@ (Pawel Dolega) Merci! vous avez soulevé plusieurs points valables qui permettront d'améliorer la conception, merci! selon votre question Je pense que le service Pluggable simplifie énormément l'utilisation des services sur le Play-side. Je voulais dire s'il y avait un moyen de simplifier, c'est-à-dire d'éviter le code standard à l'intérieur du Pluggable, il s'agit essentiellement de déléguer au service réel impliquer toutes les méthodes, par ex. ce serait génial si le 'utilisateur: UserRow' serait implicite et toutes les méthodes auto-déléguées au' UserService' sans avoir à écrire explicitement la délégation –