2016-12-16 8 views
0

Я использую Play-Slick версии 2.5.x и 3.1.x соответственно. Я использую генератор кода Slick и создаю модель Slick из существующей базы данных. На самом деле я стесняюсь признать, что я управляюсь DB-дизайном, а не управляем классом.Play-Slick: возможно ли улучшить этот дизайн (рисунок) ... и как его называть?

Это начальная установка:

  • Сформирован Slick модель под generated.Tables._
  • реализация Generic Slick дао
  • Service слой, который строит на вершине Generic Slick дао

Они являются силы, лежащие за шаблоном, который я временно называл «подключаемым сервисом», потому что он позволяет подключать функциональность уровня обслуживания к модели:

  • Контроллеры и представления Playa должны видеть только Сервисный уровень (а не Дао), например. UserService
  • Сгенерированная модель, например. UserRow, как ожидается, будет соответствовать интерфейсам бизнес-уровня, например. Тема Deadbolt-2, но не реализует ее напрямую. Чтобы иметь возможность реализовать это, нужно «слишком много», например. тип модели UserRow, UserDao и потенциально некоторый бизнес-контекст.
  • Некоторые из методов UserService естественно применимы к образцу UserRow экземпляра, например. loggedUser.roles или loggedUser.changePassword

Поэтому у меня есть:

generated.Tables.scala Slick классы модели:

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

dao.UserDao.scala 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) 
    } 
    }         

Есть ли способ Scala, который может помочь не писать код шаблона в объекте «Подключаемый сервис»? действительно ли имеет значение имя подключаемого сервиса?

+2

Этот вопрос относится к http://codereview.stackexchange.com/ – Odomontois

+0

Это вопрос дизайна, код для иллюстрации ... кроме того, активность Scala в codereview близка к несуществующей. –

+0

Мне не хватает одной вещи здесь: почему 'findUserInSession' не может получить' User' вместе с 'role' - что-то вроде' UserWithRoles (user: UserRow, role: List [Role]) '? –

ответ

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 экземпляр. Ну, вам нужно будет явно потребовать его в вашем конструкторе контроллера (точно так же, как и с DeadboltActions). В качестве альтернативы вы можете связать DeadboltActions, UserService и что еще в одном классе (например, ControllerContext?) И вставить этот единственный экземпляр в качестве одного параметра конструктора (но это, вероятно, еще одно обсуждение ...).

После этого ваш код контроллера будет выглядеть так:

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

как user и request неявно, который помогает перейти в во внутренние части вашего приложения (что часто бывает - вы приносите user объект выполнить некоторую бизнес-логику).

Это не избавиться от вашего PluggableUserService сама по себе (логика все же есть), но это может помочь вам легче повторно использовать ту же логику везде в контроллерах (как в моем опыте, вы должны иметь как User вместе с Roles чаще всего в каком-либо реальном приложении).

EDIT: У меня возникло ощущение, что я не совсем понял ваш вопрос. Вы хотите избежать шаблона в PluggableUserService или хотите избежать рассеяния этого преобразования с использованием PluggableUserService везде, в каждом контроллере (IMHO 2nd option - это то, чего следует избегать)?

+1

Не забывайте, вы можете получить пользователя в качестве опции от аутентифицированного запроса. –

+0

@ (Стив Chaloner) приятно! хорошо знать! –

+0

@ (Pawel Dolega) Спасибо! вы подняли несколько действительных точек, которые помогут улучшить дизайн, спасибо! по вашему вопросу. Я думаю, что Pluggable Service упрощает использование сервисов на Play-side. Я имел в виду, если бы был способ упростить, т. Е. Избегать кода шаблона внутри Pluggable, он в основном делегирует фактический сервис impl каждый метод, например. было бы замечательно, если бы пользователь: UserRow был бы неявным и все методы автоматически делегированы в «UserService» без необходимости явно писать делегацию –