6
votes

I try to migrate application to Slick 3.0. I would like to make a transaction for Slick 3.0. I know how to do it but I would like to ask for classes structure. Please take a look on sample repositories:

Some repositories (or DAO) for Slick 2.1:

class UserRepository {
  def all()(implicit: Session): Seq[User] = users.run
  def insert(user: User)(implicit: Session): Int = users.insert(user)
  ...
}

class CarRepository {
  def all()(implicit: Session): Seq[Car] = cars.run
  def insert(car: Car)(implicit: Session): Int = cars.insert(car)
  ...
}

In order to do the transaction in Slick 2.1 I could create a service where I can do the transaction:

db.withTransaction{ implicit session =>
     userRepository.insert(user)
     carRepository.insert(car)
}

so currently I have repositories (or DAOs) for database access and services for more general logic.

Some repositories (or DAO) for Slick 3.0:

class UserRepository {
  def all(): Future[Seq[User]] = db.run(Users.result)
  def insert(user: User): Future[Int] = db.run(Users += user)

  ...
}

class CarRepository {
  def all(): Future[Seq[Car]] = db.run(Cars.result)
  def insert(car: Car): Future[Int] = db.run(Cars += car)
  ...
}

In Slick 3.0 we can do the transaction on DBIOActions but when we have the structure as shown above it is not possible because of futures. I could create some UserCarRepository class to do the transaction but I think it is not the best one. In order to overcome that situation I expose DBIOActions in repositories (or DAOs) and then in other layer mix DBIOActions from User and Car repositories in one transaction to return Future at the end (next layer could be a service to operate on futures). When we have more repositories for transaction it could like a little bit messy.

How to structure this for Slick 3.0? How to gain more loose coupling for the transaction on different repositories?

Reading: https://github.com/playframework/play-slick/tree/master/samples https://github.com/slick/slick/issues/1084 https://groups.google.com/forum/#!topic/scalaquery/32cO7lHbxOs

1

1 Answers

1
votes

Keep the inserts as DBIOActions as long as possible, compose them as needed and then do the actual DB-query. Sketch:

class UserRepository {
  def all() = Users.result
  def insert(user: User) = Users += user

  ...
}

class CarRepository {
  def all() = Cars.result
  def insert(car: Car) = Cars += car
  ...
}

val composedInserts = (for {
    _ <- new UserRepository().insert(user)
    _ <- new CarRepository().insert(car)
} yield ()).result

db.run(composedInserts.transactionally)

Edit: clarified message