Molecule is a library to write type-inferred Scala code that translates to queries for various databases.
An sbt-molecule plugin generates boilerplate code from your domain data model definition so that you can write intuitive type-inferred queries with the words of your domain.
- Uniformly works with databases from various query languages:
- Datalog: Datomic
- SQL: H2 and PostgreSQL (more JDBC-compliant databases to come)
- Targets Scala 3.3, 2.13 and 2.12 on JVM and JS platforms
- Single SPI of 1300+ tests adhered to by each database implementation
- No macros
- No complex type class implicits
- Maximum type inference
- Synchronous/Asynchronous/ZIO APIs
- Nested data structures
- Validation
- Pagination (offset/cursor)
- Sorting
- Subscriptions and more...
Documentation at scalamolecule.org still documents the old macro-based version of molecule but will be updated to the new version. Most concepts overlap.
Examples
Same molecule query in all APIs returns the same data in different type wrappings:
Synchronous API, Datomic
import molecule.datalog.datomic.sync._
val persons: List[(String, Int, String)] =
Person.name.age.Adress.street.query.get
Synchronous API, H2
import molecule.sql.jdbc.sync._
val persons: List[(String, Int, String)] =
Person.name.age.Adress.street.query.get
Asynchronous API
import molecule.datalog.datomic.async._
val persons: Future[List[(String, Int, String)]] =
Person.name.age.Adress.street.query.get
ZIO API
import molecule.datalog.datomic.zio._
val persons: ZIO[Conn, MoleculeError, List[(String, Int, String)]] =
Person.name.age.Adress.street.query.get
Save one entity
Person.name("Bob").age(42).Adress.street("Baker st").save.transact
Insert multiple entities
Person.name.age.Adress.street.insert(
("Bob", 42, "Baker st"),
("Liz", 38, "Bond road")
).transact
Update
Person(bobId).age(43).update.transact
Delete
Person(bobId).delete.transact
Get started
Please clone molecule-samples and use one of the template projects to get started.
git clone https://github.com/scalamolecule/molecule-samples.git
Basic sbt setup
Add the following to your build files:
project/build.properties
:
sbt.version=1.9.4
project/plugins.sbt
:
addSbtPlugin("org.scalamolecule" % "sbt-molecule" % "1.3.0")
build.sbt
:
lazy val yourProject = project.in(file("app"))
.enablePlugins(MoleculePlugin)
.settings(
libraryDependencies ++= Seq(
// One or more of:
"org.scalamolecule" %%% "molecule-datalog-datomic" % "0.3.0",
"org.scalamolecule" %%% "molecule-sql-h2" % "0.3.0",
"org.scalamolecule" %%% "molecule-sql-postgres" % "0.3.0",
),
moleculeSchemas := Seq("app") // paths to your data model definitions...
)
Explore
The coreTests
module in this repo has sevel data model definitions and more than 1300 tests that you can get inspiration from.
Run jvm tests
Run the same test suite on jvm targeting both Datomic and JDBC-compliant databases:
sbt datalogDatomicJVM/test
sbt sqlH2JVM/test
sbt sqlPostgresJVM/test
Run js tests
To run tests from the client side with Scala.js, first run a jvm server (Akka Http) in one process:
sbt datalogDatomicJVM/run
Then in another process/terminal window:
sbt datalogDatomicJS/test
(Scalajs tests don't work with Scala 3.x yet)
Publish locally
To be completely up-to-date, you can pull the latest changes from Github and publish molecule locally (for Scala 2.13):
git pull
sbt publishLocal
Or target other Scala versions:
sbt ++3.3.1 publishLocal
sbt ++2.12.18 publishLocal
Author
Marc Grue
License
Molecule is licensed under the Apache License 2.0