使用光滑的

时间:2016-12-29 12:01:00

标签: scala domain-driven-design slick

我正试图在光滑中实现一个简单的聚合根。 但我真的不知道最好的方法是什么。

这是我的域名对象:

case class Project(id: UUID,
               name: String,
               state: ProjectState,
               description: String,
               team: String,
               tags: Set[String] 

我想存储"标签"在一个单独的表格中建立"项目"来自" projects_table"的对象和" project_tags_table"

这是我的表定义:

class ProjectTable(tag: Tag) extends Table[ProjectTableRecord](tag, Some("octopus_service"), "projects") {

      def id: Rep[UUID] = column[UUID]("id", O.PrimaryKey)

      def name: Rep[String] = column[String]("name")

      def state: Rep[ProjectState] = column[ProjectState]("state")

      def description: Rep[String] = column[String]("description")

      def team: Rep[String] = column[String]("team")



      override def * : ProvenShape[ProjectTableRecord] = (id, name, state, description, team, created, lastModified) <> (
        (ProjectTableRecord.apply _).tupled, ProjectTableRecord.unapply
      )
    }

class ProjectTagTable(tag: Tag) extends Table[ProjectTag](tag, Some("octopus_service"), "project_tags") {

  def projectID: Rep[UUID] = column[UUID]("project_id")

  def name: Rep[String] = column[String]("name")

  def project = foreignKey("PROJECT_FK", projectID, TableQuery[ProjectTable])(_.id, onUpdate = ForeignKeyAction.Restrict, onDelete = ForeignKeyAction.Cascade)

  override def * : ProvenShape[ProjectTag] = (projectID, name) <> (
    ProjectTag.tupled, ProjectTag.unapply
  )
}

我如何生成&#34;项目&#34;加入这两个表的对象?

提前致谢:)

1 个答案:

答案 0 :(得分:2)

我认为对责任水平存在误解。 Slick允许您访问关系数据库(在某种程度上,与SQL允许您这样做的方式相同)。它基本上是一个DAO层。

聚合根确实高于这个级别(它是域事物,而不是数据库级别的东西 - 尽管它们在很大程度上通常是相同的)。

所以基本上你需要有一个级别 Slick表,这些表允许你执行不同的查询,并且结果单个存在。

在我们开始之前 - 你应该在你的TableQuery对象的某个地方创建和存储,也许是这样的:

lazy val ProjectTable = TableQuery[ProjectTable]
lazy val ProjectTagTable = TableQuery[ProjectTagTable]

你可以把它们放在靠近你的地方Table definitions

首先,我提到你的聚合根Project需要被某种东西拉动。我们称之为ProjectRepository

我们假设它有一个方法def load(id: UUID): Future[Project]

这种方法可能如下所示:

class ProjectRepository {
    def load(id: UUID): Future[Project] = {
        db.run(
            for {
                project <- ProjectTable.filter(_.id === id).result
                tags <- ProjectTagTable.filter(_.projectId === id).result 
            } yield {
                Project(
                    id = project.id,
                    name = project.name,
                    state = project.state,
                    description = project.description,
                    team = project.team,
                    tags = tags.map(_.name)                
                )
            }
        )
    }

    // another example - if you wanted to extract multiple projects
    // (in reality you would probably apply some paging here)
    def findAll(): Future[Seq[Project]] = {
        db.run(
            ProjectTable
                .join(ProjectTag).on(_.id === _.projectId)
                .result
                .map { _.groupBy(_._1)
                        .map { case (project, grouped) =>
                             Project(
                               id = project.id,
                               name = project.name,
                               state = project.state,
                               description = project.description,
                               team = project.team,
                               tags = grouped.map(_._2.name)
                             )
                         }
                }
        )
    }
}

题外话: 如果您想在findAll方法中进行分页,则需要执行以下操作:

ProjectTable
    .drop(pageNumber * pageSize)
    .take(pageSize)
    .join(ProjectTag).on(_.id === _.projectId)
    .result

上面会产生子查询,但它基本上是你用多个连接关系进行分页的典型方式(没有子查询,你可以翻页整个结果集,这大部分时间不是你需要的!)。

回到主要部分:

显然,如果您将Project定义为:

,则会更容易
case class Project(project: ProjectRecord, tags: Seq[ProjectTag])

那么你的收益就是:

yield {
   Project(project, tags)
}

但这绝对是一种品味问题(按照你的方式制作它实际上是合理的 - 隐藏内部记录布局)。

基本上有很多东西可以在这里改进。我不是DDD的专家,但至少从Slick的角度来看,应该做的第一个改变就是改变方法:

def load(id: UUID): Future[Project]

def load(id: UUID): DBIO[Project]

并在更高级别执行db.run(...)操作。这样做的原因是,只要您Slick db.run DBIO(因此将Future转换为DBIO),您就无法在单个交易中组合多个操作。因此,一个常见的模式是在应用程序层中推高program main use mpi_f08 implicit none integer :: ierror call mpi_init(ierror) call mpi_finalize(ierror) end program main ,基本上达到定义事务边界的某些业务级别。