如何使用 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 因为接口不匹配。所以,我需要一些光:

我看过 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 实现)。