我正试图在光滑中实现一个简单的聚合根。 但我真的不知道最好的方法是什么。
这是我的域名对象:
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;加入这两个表的对象?
提前致谢:)
答案 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
,基本上达到定义事务边界的某些业务级别。