Molecule is a Scala library to build queries and transactions with the words of your domain for various databases:
Molecule generates boilerplate code from your domain data model. You can then access multiple databases in a uniform way with "molecules" like this:
val persons = Person.name.age.Address.street.query.get
The returned persons
are typed as List[(String, Int, String)]
and can also be fetched asynchronously as a Future
or a ZIO
.
Notice how the relationship from Person to Address is intuitively created. Much more complex queries can also be created
easily without having to know the query languages of the underlying databases.
- Targets Scala 3.3, 2.13 and 2.12 on JVM and JS platforms
- Typed database calls directly from Client with no need for Server implementation or JSON encoding/decoding
- Fast transparent binary serialization between Client and Server with Boopickle (no manual setup)
- Single SPI of +1800 tests adhered to by each database implementation
- No macros
- No complex type class implicits
- Maximum type inference
- Synchronous/Asynchronous/ZIO APIs
- Scala/Java types transparently mapped to all databases
- Scala primitives/wrappers
String
Int
Long
Float
Double
Boolean
Byte
Short
Char
BigInt
BigDecimal
- java.util/net
Date
UUID
URI
- java.time
Duration
Instant
LocalDate
LocalTime
LocalDateTime
OffsetTime
OffsetDateTime
ZonedDateTime
- Scala primitives/wrappers
Set
s of all above types supported as field/column type for all databases- Collision of valid Scala field/column name with reserved keywords of database transparently resolved
- Nested data structures
- Validation
- Pagination (offset/cursor)
- Sorting
- Subscriptions
Documentation at scalamolecule.org still documents the old macro-based version of molecule but will be updated to the new version. Most concepts overlap.
- Define a domain data model.
- Run
sbt compile -Dmolecule=true
once to generate molecule-enabling boilerplate code from your domain data model definition. The sbt-molecule plugin automatically also creates database schemas for all database types. Now you can easily read and write data to/from a database with plain vanilla Scala code in a fluent style (see examples below). - Write molecule transactions/queries.
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.Address.street.query.get
Synchronous API, PostgreSQL
import molecule.sql.postgres.sync._
val persons: List[(String, Int, String)] =
Person.name.age.Address.street.query.get
Asynchronous API
import molecule.sql.postgres.async._
val persons: Future[List[(String, Int, String)]] =
Person.name.age.Address.street.query.get
ZIO API
import molecule.sql.postgres.zio._
val persons: ZIO[Conn, MoleculeError, List[(String, Int, String)]] =
Person.name.age.Address.street.query.get
Save one entity
Person.name("Bob").age(42).Address.street("Baker st").save.transact
Insert multiple entities
Person.name.age.Address.street.insert(
("Bob", 42, "Baker st"),
("Liz", 38, "Bond road")
).transact
Update
Person(bobId).age(43).update.transact
Delete
Person(bobId).delete.transact
Please clone molecule-samples and use one of the template projects to get started.
git clone https://github.com/scalamolecule/molecule-samples.git
Add the following to your build files:
project/build.properties
:
sbt.version = 1.9.9
project/plugins.sbt
:
addSbtPlugin("org.scalamolecule" % "sbt-molecule" % "1.7.0")
build.sbt
:
lazy val yourProject = project.in(file("app"))
.enablePlugins(MoleculePlugin)
.settings(
libraryDependencies ++= Seq(
// One or more of:
"org.scalamolecule" %%% "molecule-document-mongodb" % "0.8.0",
"org.scalamolecule" %%% "molecule-datalog-datomic" % "0.8.0",
"org.scalamolecule" %%% "molecule-sql-h2" % "0.8.0",
"org.scalamolecule" %%% "molecule-sql-mariadb" % "0.8.0",
"org.scalamolecule" %%% "molecule-sql-mysql" % "0.8.0",
"org.scalamolecule" %%% "molecule-sql-postgres" % "0.8.0",
),
moleculeSchemas := Seq("app") // paths to your data model definitions
)
The coreTests
module in this repo has several data model definitions and more than 1800 tests that show all details of
how molecule can be used. This forms the Service Provider Interface that each database implementation needs to comply to
in order to offer all functionality of Molecule.
Make sure Docker is running to run tests for MongoDB, MariaDB, Mysql and Postgress (Datomic and H2 run in-memory). For instance on a mac you can start Docker Desktop.
Run the same test suite on jvm targeting various databases:
sbt datalogDatomicJVM/test
sbt documentMongodbJVM/test
sbt sqlH2JVM/test
sbt sqlMariadbJVM/test
sbt sqlMysqlJVM/test
sbt sqlPostgresJVM/test
To run tests from the client side with Scala.js, first run a jvm server (Akka Http) in one process:
sbt sqlPostgresJVM/run
Then in another process/terminal window:
sbt sqlPostgresJS/test
(Scalajs tests don't work with Scala 3.x yet)
To be completely up-to-date, you can pull the latest snapshot from Github.
Initially you clone the sbt-molecule
and molecule
repositories
git clone https://github.com/scalamolecule/sbt-molecule.git
cd ..
git clone https://github.com/scalamolecule/molecule.git
And hereafter you can just pull the latest changes in each repository directory
cd sbt-molecule
git pull
cd ../molecule
git pull
To generate the boilerplate code with the latest plugin, run the following commands:
cd molecule
sbt ++2.12.19 "project baseJVM" publishLocal # Used by sbt-molecule
cd ../sbt-molecule
sbt publishLocal # Make the plugin available
cd ../molecule
sbt compile -Dmolecule=true # Generate boilerplate code
Now the boilerplate code for the core tests is generated and the various test suites can be run from your IDE (be prepared that it takes a while to compile all the tests for all SPI implementations).
Marc Grue
Molecule is licensed under the Apache License 2.0