Reputation: 632
I have a question about persistence layer abstraction.
Create a structure not streactly Slick related, therefore Entities must be disjointed from Slick and Slick-repo plug-in.
Environment
- Scala % 2.12.4
- com.typesafe.play % sbt-plugin % 2.6.12
- com.typesafe.play % play-slick % 3.0.3
- com.byteslounge % Slick-repo % 1.4.3
At the moment i have created abstraction with the following traits:
IRepository:
trait IRepository[E <: Entity[E, ID], ID] {
type Transaction[T]
val transactionManager: TransactionManager[Transaction]
final type EntityType = E
def findAll()(implicit ec: ExecutionContext): Transaction[Seq[E]]
def findOne(id : ID)(implicit ec: ExecutionContext): Transaction[Option[E]]
def lock(entity: E)(implicit ec: ExecutionContext): Transaction[E]
def save(entity: E)(implicit ec: ExecutionContext): Transaction[E]
def batchInsert(entities: Seq[E]): Transaction[Option[Int]]
def update(entity: E)(implicit ec: ExecutionContext): Transaction[E]
def delete(entity: E)(implicit ec: ExecutionContext): Transaction[E]
def count(): Transaction[Int]
def exclusiveLockStatement(sql: String): String
}
TransactionManager:
trait TransactionManager[T[_]] {
final type Transaction[Entity] = T[Entity]
implicit def executeAll[Entity](op: Seq[T[Entity]]): Future[Seq[Entity]]
implicit def execute[Entity](op: T[Entity]): Future[Entity]
}
Entity:
trait Entity[T <: Entity[T, ID], ID] {
final type IdType = ID
val id: Option[ID]
def withId(id: ID): T
}
In the services i'm using implicit conversion scala feature to execute Db actions and obtains Future instances
This is a simple example:
class UserServiceImpl @Inject()(private val repo: UserRepository)
(implicit ec: ExecutionContext) extends UserService {
import repo.transactionManager._
override def retrieve(id: Long): Future[Option[User]] =
repo.findOne(id)
override def save(user: User): Future[User] =
repo.save(user)
override def retrieve(loginInfo: LoginInfo): Future[Option[User]] =
repo.findByLoginInfo(loginInfo)
override def delete(user: User): Future[User] =
repo.delete(user)
}
All these abstractions needs implementations and here the problems begin.
I think i have to give up on repositories abstraction because obliviously to use slick BDIO methods i need to have concrete access to that and this tie my code to Slick but i think it's ok.
Entities are not related to slick with my trait(Entity) but the troubles are on integration with repos.
My slick repos extends Repository class from Slick-repo plug-in, these class(like my IRepository trait) require two generic type, the first must extend a trait named Entity(Slick-repo's trait) while the second have no constraints and represent the id type of the entity.
In summary:
My classes
- IRepository[Entity, ID]
- Entity[Entity, ID]
- SlickRepository[Entity, ID] extends IRepository
Slick-repo classes
- Repository[Entity, ID]
- Entity[Entity, ID]
SlickRepository extends/should use Repository(Slick-repo) but that class want that the first generic parameter exends Entity(Slick-repo) while in my case, for separation, it's extends Entity trait(my domain trait)
i hope it's clear, i know that the situation is a litle complex especially because some class have the same name
Maybe my concept of this problem is totally wrong i'don't know i hope someone can help me.
Thanks to all in advices.
Upvotes: 1
Views: 276
Reputation: 11
You can check Slick's Persistent Layer abstraction example here: https://github.com/gonmarques/slick-repo.
This repo provides common features like:
Provide common database operations like save, update, find, delete or count in a type-safe way
Other operations like Transactions, Batch Insert, Optimistic Locking (aka versioning),
Pessimistic Locking or custom query/statement execution is also supported
In order to maximize performance, all provided operations are backed by Slick compiled queries, as recommended in Slick Documentation
Upvotes: 1