如何使用Slick 3.1和DDD实现多数据库模式

时间:2016-11-27 22:28:17

标签: scala functional-programming domain-driven-design slick ddd-repositories

我正在阅读Debasish Ghosh的Functional And Reactive Domain Modeling,我想重构一个CRUD应用程序,它实际上是在生产中。 我专注于第一种方法,使用Reader monad for DI,Repository模式和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 multi-db实现,我可以与Debasish公开的范例一起使用?
  • 我如何改进我的具体实现的设计,以便符合本书中定义的存储库模式,并且还没有弄乱现在的具体和抽象实现? (保持关注点分开)

我见过the Multi-DB example that Lightbend has,但是,除了未解决的依赖问题之外,它还很大程度上依赖于Cake Pattern,这非常冗长。我试图坚持这本书。

任何帮助将不胜感激。

由于

1 个答案:

答案 0 :(得分:1)

您遇到此类错误的原因是您没有混合OracleDB。你应该替换:

class EquivalenceOracleRepository extends EquivalenceRepository with EquivalenceOracleDB

class EquivalenceOracleRepository extends EquivalenceRepository with EquivalenceOracleDB with OracleDB

现在您将收到db未定义的错误,因此您应该在某处提供其实现(因为它在DBComponent中定义为抽象)。我认为这可以在OracleDB中完成,这将使该特征具体而不是现在的抽象。我认为这样你将或多或少地符合本书中提出的设计(你有一个描述你的存储库的抽象接口,以及多个具体/生产实现)。