如何使用 Slick 3.1 和 DDD 实现多数据库模式
How to implement Multi-DB Pattern with Slick 3.1 and DDD
我正在阅读 Debasish Ghosh 的 Functional And Reactive Domain Modeling,我想重构一个实际在生产中的 CRUD 应用程序。
我专注于使用 Reader monad 的第一种方法,用于 DI、存储库模式和 ADT,用于管理应用程序之间的等价值的应用程序(想想一个值,以某种方式被某些应用程序理解,然后查询它是等价的另一个系统的价值)。
Debasish 将存储库模式称为一种解耦方式,因此,就我而言,我需要 Oracle 和 Postgresql 以及 H2 的具体实现来进行测试。
我有以下非常简单的实现(基于本书):
存储库的基础 trait
:
trait Repository[A, Id] {
def query(id: Id): Try[Option[A]]
def insert(a: A): Try[A]
def update(a: A): Try[A]
def delete(a: A): Try[A]
}
等效库模块:
trait EquivalenceRepository extends Repository[Equivalence, Long]{
def query(id: Long): Try[Option[Equivalence]]
def insert(a: Equivalence): Try[Equivalence]
def update(a: Equivalence): Try[Equivalence]
def delete(a: Equivalence): Try[Equivalence]
}
以及中途具体实现与Slick:
class EquivalenceOracleRepository extends EquivalenceRepository {
def query(id: Long): Try[Option[Equivalence]] = {
???
}
def insert(a: Equivalence): Try[Equivalence] = {
???
}
def update(a: Equivalence): Try[Equivalence] = {
???
}
def delete(a: Equivalence): Try[Equivalence] = {
???
}
}
private[repository] trait EquivalenceOracleDB{
this: DBComponent =>
import jdbcProfile.api._
final case class EquivalenceDTO(
originId: Int,
equivalenceId: Int,
creator: String,
creationDate: Timestamp,
isActive: Boolean
)
final class EquivalenceTable(tag: Tag) extends Table[Equivalence](tag, "Equivalence"){
def originId: Rep[Int] = column[Int]("ORIGIN_ID", O.SqlType("NUMBER(10)"))
def equivalenceId: Rep[Int] = column[Int]("EQUIVALENCE_ID", O.SqlType("NUMBER(10)"))
def creator: Rep[String] = column[String]("CREATOR", O.SqlType("NUMBER(10)"))
def creationDate: Rep[Timestamp] = column[Timestamp]("CREATION_DATE", O.SqlType("TIMESTAMP(6)"))
def isActive: Rep[Boolean] = column[Boolean]("IS_ACTIVE", O.SqlType("VARCHAR2(1)"))
def pk: PrimaryKey = primaryKey("EQUIVALENCES_PK", (originId, equivalenceId))
def * : ProvenShape[EquivalenceDTO] =
(originId, equivalenceId, creator, creationDate, isActive) <> (EquivalenceDTO.tupled, EquivalenceDTO.unapply)
}
val table = TableQuery[EquivalenceTable]
}
作为 Oracle 的最后一个具体实现,您可以看到该特征需要一个 DBComponent。这是一个特征,它的代码是从实际生产应用程序继承的,并试图为每个 DBMS 定义具体的 Slick 配置文件:
这是对每个 DBMS 的分析:
trait Profile {
val jdbcProfile: JdbcProfile
}
object OracleProfile extends Profile {
override val jdbcProfile: JdbcProfile = OracleDriver
}
object H2Profile extends Profile {
override val jdbcProfile: JdbcProfile = H2Driver
}
object PostgreSQLProfile extends Profile {
override val jdbcProfile: JdbcProfile = PostgreSQLProfile.jdbcProfile
}
这是数据库定义:
trait DBComponent {
val jdbcProfile: JdbcProfile
import jdbcProfile.api._
val db: Database
}
trait OracleDB extends DBComponent {
val logger: Logger = LoggerFactory.getLogger(this.getClass)
val jdbcProfile: JdbcProfile = OracleProfile.jdbcProfile
}
trait H2DB extends DBComponent {
val logger: Logger = LoggerFactory.getLogger(this.getClass)
val jdbcProfile: JdbcProfile = H2Profile.jdbcProfile
}
trait PostgreSQLDB extends DBComponent {
val logger: Logger = LoggerFactory.getLogger(this.getClass)
val jdbcProfile: JdbcProfile = PostgreSQLProfile.jdbcProfile
}
但我的疑虑来了:如果我尝试将包含 Slick 基础知识的 EquivalenceOracleDB
特征混合到 EquivalenceOracleRepository
中,我也需要混合该组件,但实际上我遇到了一个错误:
混音:
class EquivalenceOracleRepository extends EquivalenceRepository with EquivalenceOracleDB{
错误:Illegal inheritance, self-type EquivalenceOracleRepository does not conform to DBComponent
因为接口不匹配。所以,我需要一些光:
- 是否有一个已知的具体 Slick 多数据库实现,我可以将其与 Debasish 公开的范例结合使用?
- 我怎样才能改进我的具体实现的设计以符合书中定义的存储库模式,同时又不弄乱现在的具体和抽象实现? (保持关注分离)
我看过 the Multi-DB example that Lightbend has,但是,除了未解决的依赖性问题外,它严重依赖于非常冗长的 Cake 模式。我正在努力坚持这本书。
任何帮助将不胜感激。
谢谢
您收到此类错误的原因是您没有混合 OracleDB
。您应该更换:
class EquivalenceOracleRepository extends EquivalenceRepository with EquivalenceOracleDB
和
class EquivalenceOracleRepository extends EquivalenceRepository with EquivalenceOracleDB with OracleDB
现在你会得到一个错误,说 db
没有定义,所以你应该在某处提供它的实现(因为它在 DBComponent
中被定义为抽象的)。我认为这可以在 OracleDB
中完成,这将使该特征具体化,而不是像现在这样抽象。我认为这样你或多或少会符合书中介绍的设计(你有一个描述你的存储库的抽象接口,以及多个 concrete/production 实现)。
我正在阅读 Debasish Ghosh 的 Functional And Reactive Domain Modeling,我想重构一个实际在生产中的 CRUD 应用程序。 我专注于使用 Reader monad 的第一种方法,用于 DI、存储库模式和 ADT,用于管理应用程序之间的等价值的应用程序(想想一个值,以某种方式被某些应用程序理解,然后查询它是等价的另一个系统的价值)。
Debasish 将存储库模式称为一种解耦方式,因此,就我而言,我需要 Oracle 和 Postgresql 以及 H2 的具体实现来进行测试。 我有以下非常简单的实现(基于本书):
存储库的基础 trait
:
trait Repository[A, Id] {
def query(id: Id): Try[Option[A]]
def insert(a: A): Try[A]
def update(a: A): Try[A]
def delete(a: A): Try[A]
}
等效库模块:
trait EquivalenceRepository extends Repository[Equivalence, Long]{
def query(id: Long): Try[Option[Equivalence]]
def insert(a: Equivalence): Try[Equivalence]
def update(a: Equivalence): Try[Equivalence]
def delete(a: Equivalence): Try[Equivalence]
}
以及中途具体实现与Slick:
class EquivalenceOracleRepository extends EquivalenceRepository {
def query(id: Long): Try[Option[Equivalence]] = {
???
}
def insert(a: Equivalence): Try[Equivalence] = {
???
}
def update(a: Equivalence): Try[Equivalence] = {
???
}
def delete(a: Equivalence): Try[Equivalence] = {
???
}
}
private[repository] trait EquivalenceOracleDB{
this: DBComponent =>
import jdbcProfile.api._
final case class EquivalenceDTO(
originId: Int,
equivalenceId: Int,
creator: String,
creationDate: Timestamp,
isActive: Boolean
)
final class EquivalenceTable(tag: Tag) extends Table[Equivalence](tag, "Equivalence"){
def originId: Rep[Int] = column[Int]("ORIGIN_ID", O.SqlType("NUMBER(10)"))
def equivalenceId: Rep[Int] = column[Int]("EQUIVALENCE_ID", O.SqlType("NUMBER(10)"))
def creator: Rep[String] = column[String]("CREATOR", O.SqlType("NUMBER(10)"))
def creationDate: Rep[Timestamp] = column[Timestamp]("CREATION_DATE", O.SqlType("TIMESTAMP(6)"))
def isActive: Rep[Boolean] = column[Boolean]("IS_ACTIVE", O.SqlType("VARCHAR2(1)"))
def pk: PrimaryKey = primaryKey("EQUIVALENCES_PK", (originId, equivalenceId))
def * : ProvenShape[EquivalenceDTO] =
(originId, equivalenceId, creator, creationDate, isActive) <> (EquivalenceDTO.tupled, EquivalenceDTO.unapply)
}
val table = TableQuery[EquivalenceTable]
}
作为 Oracle 的最后一个具体实现,您可以看到该特征需要一个 DBComponent。这是一个特征,它的代码是从实际生产应用程序继承的,并试图为每个 DBMS 定义具体的 Slick 配置文件:
这是对每个 DBMS 的分析:
trait Profile {
val jdbcProfile: JdbcProfile
}
object OracleProfile extends Profile {
override val jdbcProfile: JdbcProfile = OracleDriver
}
object H2Profile extends Profile {
override val jdbcProfile: JdbcProfile = H2Driver
}
object PostgreSQLProfile extends Profile {
override val jdbcProfile: JdbcProfile = PostgreSQLProfile.jdbcProfile
}
这是数据库定义:
trait DBComponent {
val jdbcProfile: JdbcProfile
import jdbcProfile.api._
val db: Database
}
trait OracleDB extends DBComponent {
val logger: Logger = LoggerFactory.getLogger(this.getClass)
val jdbcProfile: JdbcProfile = OracleProfile.jdbcProfile
}
trait H2DB extends DBComponent {
val logger: Logger = LoggerFactory.getLogger(this.getClass)
val jdbcProfile: JdbcProfile = H2Profile.jdbcProfile
}
trait PostgreSQLDB extends DBComponent {
val logger: Logger = LoggerFactory.getLogger(this.getClass)
val jdbcProfile: JdbcProfile = PostgreSQLProfile.jdbcProfile
}
但我的疑虑来了:如果我尝试将包含 Slick 基础知识的 EquivalenceOracleDB
特征混合到 EquivalenceOracleRepository
中,我也需要混合该组件,但实际上我遇到了一个错误:
混音:
class EquivalenceOracleRepository extends EquivalenceRepository with EquivalenceOracleDB{
错误:Illegal inheritance, self-type EquivalenceOracleRepository does not conform to DBComponent
因为接口不匹配。所以,我需要一些光:
- 是否有一个已知的具体 Slick 多数据库实现,我可以将其与 Debasish 公开的范例结合使用?
- 我怎样才能改进我的具体实现的设计以符合书中定义的存储库模式,同时又不弄乱现在的具体和抽象实现? (保持关注分离)
我看过 the Multi-DB example that Lightbend has,但是,除了未解决的依赖性问题外,它严重依赖于非常冗长的 Cake 模式。我正在努力坚持这本书。
任何帮助将不胜感激。
谢谢
您收到此类错误的原因是您没有混合 OracleDB
。您应该更换:
class EquivalenceOracleRepository extends EquivalenceRepository with EquivalenceOracleDB
和
class EquivalenceOracleRepository extends EquivalenceRepository with EquivalenceOracleDB with OracleDB
现在你会得到一个错误,说 db
没有定义,所以你应该在某处提供它的实现(因为它在 DBComponent
中被定义为抽象的)。我认为这可以在 OracleDB
中完成,这将使该特征具体化,而不是像现在这样抽象。我认为这样你或多或少会符合书中介绍的设计(你有一个描述你的存储库的抽象接口,以及多个 concrete/production 实现)。