Scala meta-DSL for the Datomic database
Molecule is a Scala library to query SQL and NoSQL databases with the words of your domain.
Compose an immutable “molecule” data structure:
Person.name.age.Address.street
instead of building queries
SQL | Datalog |
sql
SELECT
Person.name,
Person.age,
Address.street
FROM Person
INNER JOIN Address
ON Person.address = Address.id; | clojure
[:find ?name ?age ?street
:where [?a :Person/name ?name]
[?a :Person/age ?age]
[?a :Ns/address ?b]
[?b :Address/street ?street]] |
and get typed data matching the molecule from the database:
val persons: List[(String, Int, String)] =
Person.name.age.Address.street.query.get
Data can also be fetched asynchronously in a Future
or ZIO
.
1) Define a model of your domain data with Molecule’s meta DSL
object MyDomain extends DataModel(5) {
trait Person {
val name = oneString
val age = oneInt
val birthday = oneLocalDate
val address = one[Address]
}
trait Address {
val street = oneString
val zip = oneString
val city = oneString
}
}
2) Run sbt compile -Dmolecule=true
once to generate molecule-enabling boilerplate code and db schemas.
3) Compose fluent molecules with your domain terms to save and read data from your database.
Molecules using any Database/API combination return the same data, just in different 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
IO API
import molecule.sql.postgres.io._
val persons: cats.effect.IO[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
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.10.7
project/plugins.sbt
:
addSbtPlugin("org.scalamolecule" % "sbt-molecule" % "1.12.0")
build.sbt
:
lazy val yourProject = project.in(file("app"))
.enablePlugins(MoleculePlugin)
.settings(
libraryDependencies ++= Seq(
// One or more of:
"org.scalamolecule" %%% "molecule-sql-postgres" % "0.16.0",
"org.scalamolecule" %%% "molecule-sql-sqlite" % "0.16.0",
"org.scalamolecule" %%% "molecule-sql-mysql" % "0.16.0",
"org.scalamolecule" %%% "molecule-sql-mariadb" % "0.16.0",
"org.scalamolecule" %%% "molecule-sql-h2" % "0.16.0",
"org.scalamolecule" %%% "molecule-datalog-datomic" % "0.16.0",
),
// Paths to directories with your domain structure definition files
moleculeDomainPaths := Seq("app")
)
The coreTests
module in this repo has several data model definitions and +1700 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.
sbt-molecule
and molecule
repositoriesMarc Grue
Molecule is licensed under the Apache License 2.0