使用 Play Framework + Scala + Slick 将业务逻辑从控制器分离到模型层的最佳实践



我是使用Play框架的新手。我正在努力设计一个一对多的模型并使用 Json 检索它。我正在使用Play 2.6,Scala 2.11和Slick 3.0.0。我有两个表:Rackcase class RackRow(id: String, produced: Float, currentHour: Long)Gpucase class Gpu(id: String, rackId: String, produced: Float, installedAt: String)

我以前的错误之一是将业务逻辑放在控制器层中。因此,我正在将业务逻辑移动到模型中。以下是我使用 Slick 的RackRepository.scala: case class Rack(id: String, production: Float, currentHour: String, gpuList: Seq[Gpu])

case class RackRow(id: String, produced: Float, currentHour: Long)
case class RackException(message: String) extends Exception(message)
class RackRepository @Inject()(protected val dbConfigProvider: DatabaseConfigProvider, gpuRepository: GpuRepository)
(implicit ec: ExecutionContext) extends HasDatabaseConfigProvider[JdbcProfile] {
import profile.api._
lazy val RackTable = new TableQuery(tag => new RackTable(tag))
def getProfile: JdbcProfile = profile
def database: JdbcBackend#DatabaseDef = db
def create(row: List[RackRow]): Future[Option[Int]] =
db.run(RackTable ++= row)
def insert(row: RackRow): Future[Unit] =
db.run(RackTable += row).map(_ => ())
def updateProduced(rackId: String, produced: Float): Future[Unit] =
db.run(RackTable.filter(_.id === rackId).map(r => r.produced).update(produced)).map(_ => ())
def updateRack(rackId: String, produced: Float, currentHour: Long): Future[Unit] =
db.run(RackTable.filter(_.id === rackId).map(r => (r.produced, r.currentHour)).update(produced, currentHour)).map(_ => ())
def updateRackProduced(id: String): Future[Unit] = {
gpuRepository.getByRack(id).map { seqGpuRow: Seq[GpuRow] =>
val total: Float = seqGpuRow.map(_.produced).sum
update(id, Some(total), Some(System.currentTimeMillis))
}
}
def update(rackId: String, produced: Option[Float], currentHour: Option[Long]): Future[Unit] = {
(produced, currentHour) match {
case (Some(produced), Some(currentHour)) =>
db.run(RackTable.filter(_.id === rackId).map(r => (r.produced, r.currentHour)).update(produced, currentHour)).map(_ => ())
case (Some(produced), None) =>
db.run(RackTable.filter(_.id === rackId).map(r => r.produced).update(produced)).map(_ => ())
case (None, Some(currentHour)) =>
db.run(RackTable.filter(_.id === rackId).map(r => r.currentHour).update(currentHour)).map(_ => ())
case (None, None) => Future("update not executed.")
}
}
def listAllRacksWithSetup(): Future[Setup] = {
list().flatMap { seqRackRow: Seq[RackRow] =>
val futureSeqRackRow: Seq[Future[Rack]] = seqRackRow.map { rackRow: RackRow =>
gpuRepository.getByRack(rackRow.id).map { seqGpuRow: Seq[GpuRow] =>
val seqGpu = seqGpuRow.map(gpuRepository.gpuRowToGpu) // return Seq[Gpu]
Rack(rackRow.id, rackRow.produced, Util.toDate(rackRow.currentHour), seqGpu)
} // return Future[Rack]
}
val futureSeqRack: Future[Seq[Rack]] = Future.sequence(futureSeqRackRow)
futureSeqRack.map(racks => Setup(getProfitPerGpu, racks))
}
}
}

如果你看到类RackRepository的构造函数,我正在传递参数gpuRepository: GpuRepository,即另一个模型。我想这种方法是不对的。但是我这样做是为了使用def listAllRacksWithSetup()gpuRepository.getByRack()的方法。 当我转到另一个模型GpuRepository时,问题就来了,我无法在构造函数中传递rackRepository: RackRepository,因为它抛出了有关递归依赖的错误。所以,这就是为什么我的方法不正确。 如何设计RackRepositoryGpuRepository一个可以访问另一个?如果根据最佳实践,这是正确的。

我想在提出问题时,我意识到答案是什么。 但是感谢所有阅读我问题的人... 我在GpuRepository内创建了一个lazy val rackRepository = new RackRepository(dbConfigProvider)。我想知道这是否仍然是 Play 框架的好做法。如果有人仍然可以回答Play框架中MVC的最佳实践。 谢谢

class GpuRepository @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)
(implicit ec: ExecutionContext) extends HasDatabaseConfigProvider[JdbcProfile] {
import profile.api._
lazy val GpuTable = new TableQuery(tag => new GpuTable(tag))
lazy val rackRepository = new RackRepository(dbConfigProvider)

最新更新