0
votes

Our app has a service layer on top of a storage layer - a service layer method will create a new database session (we're using Slick on top of MySql) and passes the session as an implicit parameter to the storage layer methods

trait Service1 {
  def serviceLayerMethod(params) = {
    db withSession {
      implicit session: Session =>
        storageLayerMethod1(params)
        storageLayerMethod2(params)
}}}

object DAO1 {
  def storageLayerMethod1(params)(implicit session: Session)
}

object DAO2 {
 def storageLayerMethod2(params)(implicit session: Session)
}

We'd like to be able to inject different method implementations from the service layer to the storage layer, for example we have a multiGet method that retrieves multiple records, and we'd like to have different implementations of this method e.g. one that does the multiGet in parallel and another that attempts to retrieve data from the Redis cache before pulling it from the database. I could pass these methods around as implicit parameters, but I was hoping that there was a way to do this with less boilerplate.

trait MultiGet {
  def multiGet(params)(implicit session: Session)
}

object MultiGetDefault extends MultiGet
object MultiGetParallel extends MultiGet
object MultiGetCached extends Multiget

trait Servic1 {
  def serviceLayerMethod1(params) = {
    db withSession {
      implicit session: Session =>
        storageLayerMethod1(params)(MultiGetDefault)
  }}

  def serviceLayerMethod2(params) = {
    db withSession {
      implicit session: Session =>
        storageLayerMethod1(params)(MultiGetParallel)
}}}

object DAO1 {
  def storageLayerMethod1(params)(implicit session: Session, multiGetImpl: MultiGet) {
    multiGetImpl.multiGet(params)
}}

Most of the storage layer methods are in singleton objects, so I'm not able to mixin different MultiGet implementations without a significant refactor. The service layer traits are being instantiated/injected at the controller layer via the cake pattern.

1
Maybe this answer would suit your needs: stackoverflow.com/questions/21965848/…yǝsʞǝla

1 Answers

0
votes

We're going to try extending/wrapping Session with a version that implements multiGet etc

trait MySession extends Session {
  private val session: Session

  def capabilities = session.capabiliites
  def close = session.close
  def conn = session.conn
  ...

  def multiGet(tableName: String, ids: Seq[Int])
}

class DefaultSession(private val session: Session) extends MySession {
  def multiGet(tableName: String, ids: Seq[Int]) // default implementation
}

class ConcurrentSession(private val session: Session) extends MySession {
  def multiGet(tableName: String, ids: Seq[Int]) // concurrent implementation
}

def defaultExecute[T](fun: (MySession) => T): T = db withSession {
  _session: Session =>
    fun(new DefaultSession(_session))
}

def concurrentExecute[T](fun: (MySession) => T): T = db withSession {
  _session: Session =>
    fun(new ConcurrentSession(_session))
}