Last active
September 9, 2019 01:30
-
-
Save cvogt/9239494 to your computer and use it in GitHub Desktop.
Slick app architecture cheat sheet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Please comment in case of typos or bugs | |
import scala.slick.driver.H2Driver._ | |
val db = Database.for...(...) | |
case class Record( ... ) | |
class Records(tag: Tag) extends Table[Record](tag,"RECORDS"){ | |
... | |
def * = ... <> (Record.tupled,Record.unapply) | |
// place additional methods here which return values of type Column | |
// (compute artificial columns based on other columns) based on a | |
// records row | |
def foo = (col1+col2, col3) | |
... | |
} | |
object records extends TableQuery(new Records(_)){ | |
// Only place methods here which return a not-yet executed Query or | |
// (individually meaningful) Column which is based on the WHOLE table. | |
// Seldom useful, often better placed in RecordQueryExentions | |
def foos = this.map(_.foo) // <- can only be used on the whole table | |
... | |
} | |
// usage: | |
db.withSession{ records.map(_.foo).run } | |
db.withSession{ records.foos.run } | |
implicit class RecordQueryExtensions(val q: Query[Records,Record]) extends AnyVal{ | |
// Only place methods here which return a not-yet executed Query or | |
// (individually meaningful) Column. | |
// Methods placed here can be chained/combined. | |
... | |
def foos = q.map(_.foo) | |
def byId(id: Column[Long]) = q.filter(_.id === id) | |
} | |
// usage: | |
db.withSession{ records.filter(_.age < 30).foos.run } | |
object RecordsDAO{ | |
// place methods here that require a database connection | |
// i.e. do not compose without executing queries, e.g. | |
// methods take Session argument | |
// usage: | |
// db.withSession{ RecordsDAO.foos } | |
// or | |
// db.withSession{ implicit session => Records.DAO.foos } | |
def foos(implicit s:Session) = records.foos.run | |
val byIdCompiled = Compiled(records.byId) | |
def byId(id: Long)(implicit s:Session) = byIdCompiled(id).run.headOption | |
... | |
} | |
// usage: | |
db.withSession{ RecordsDAO.foos } | |
db.withSession{ implicit session => Records.DAO.foos } | |
// Alternative DAO implementation: | |
case class RecordsDAO(implicit s:Session){ | |
// centralized implicit session into class argument | |
def foos = records.foos.run | |
... | |
} | |
// usage: | |
db.withSession{ RecordsDAO().foos } | |
db.withSession{ implicit session => RecordsDAO().foos } | |
// Another alternative DAO implementation: | |
object RecordsDAO{ | |
// in user code | |
// no withSession boilerplate but no | |
// control over foos / transaction management | |
def foos = db.withSession{ records.foos.run } | |
// or: | |
def foos = db.withSession{ implicit s => records.foos.run } | |
// or: | |
def foos = db.withSession{ records.foos.list()(_) } | |
... | |
} | |
// usage: | |
RecordsDAO.foos | |
// Alternative Table class implementation using a mapped projection case class | |
// Allows projection case classes to be nested and re-used in queries and multiple tables. | |
// Uses the recently born CaseClassShape suggested here: | |
// https://github.com/slick/slick/pull/692 | |
case class RecordProjection( ... : Column[...], ... : Column[...], ... ){ | |
// place additional methods here which return values of type Column | |
// (compute artificial columns based on other columns) based on a | |
// records row | |
def foo = (col1+col2, col3) | |
... | |
} | |
implicit object RecordShape extends CaseClassShape(RecordProjection.tupled,Record.tupled) | |
class Records extends Table[Record](...){ | |
def projection = RecordProjection(column(...),column(...),...) // column types can be inferred | |
def * = projection | |
} | |
val records = TableQuery[Records].map(_.projection) | |
// usage: | |
db.withSession{ records.map(_.foo).run } |
Typo on lines 19: s/RecordQueryExentions/RecordQueryExtensions/
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The advantage of the slick-dao is that it implements the basic CRUD in a DAO so we don't have to manipulate queries or any infrastructure/DB code in your domain. Nothing more than that.
I do that because I want everything to go through a DAO, basic CRUD and queries. Otherwise the domain gets cluttered with some locally defined queries, some DAOs, extensions and some on.