Setup

To use Molecule we need to define our database schema in a Schema definition file and then tell sbt about it. When compiling our project from the command line, all necessary boilerplate code is then automatically generated by the sbt MoleculePlugin.

1. SBT build settings

Add the following to your build files:

project/build.properties:

sbt.version=1.1.6

project/buildinfo.sbt:

addSbtPlugin("org.scalamolecule" % "sbt-molecule" % "0.6.2")

build.sbt:

lazy val yourProject = project.in(file("app"))
  .enablePlugins(MoleculePlugin)
  .settings(
    resolvers ++= Seq(
      "datomic" at "http://files.datomic.com/maven",
      "clojars" at "http://clojars.org/repo",
      Resolver.sonatypeRepo("releases")
    ),
    libraryDependencies ++= Seq(
      "org.scalamolecule" %% "molecule" % "0.15.1",
      "com.datomic" % "datomic-free" % "0.9.5697"
    ),
    moleculeSchemas := Seq("app") // paths to directory with your schema definition file(s)
  )

Molecule 0.15.1 for Scala 2.12.7 is available at Sonatype.

2. Paths to Schema definition files

We use the moleculeSchemas sbt settings key to tell the sbt MoleculePlugin where we have our Schema definition files.

A Schema definition file contains a plain Scala object where you define partitions/namespaces/attributes of your Datomic database. The MoleculePlugin uses the information defined there to create all the boilerplate code needed to use Molecule in your code.

You can have a single or several Schema definition files in a project. Each definition file defines a single database. This is useful if you for instance want to experiment with various database designs during development.

Schema definiton files should reside in directories named schema anywhere in your source code.

Use the moleculeSchemas sbt settings key to list the directories in your project source code that contains your schema directories.

Say you have a project demo and a single Schema definition file YourDomainDefinition.scala defining your database:

Then you simply add moleculeSchemas := Seq("app") as we saw above.

Multiple schemas

In the main Molecule project’s examples module we have several Schema definition files:

And we then list the paths to those like this in our build.sbt:

moleculeSchemas := Seq(
  "molecule/examples/dayOfDatomic",
  "molecule/examples/graph",
  "molecule/examples/mbrainz",
  "molecule/examples/seattle"
)

3. Compile

Now that you have created a schema definition file and told sbt about where to find it, you can compile your project from the terminal

> cd yourProjectRoot
> sbt compile

The MoleculePlugin will now automatically as part of the compilation process do 5 things:

  1. Generate Molecule boilerplate dsl source code files (in the src_managed directory in target)
  2. Generate a schema file with the necessary code to transact the Datomic schema
  3. Compile the generated code
  4. Package both the source code and compiled classes into two jars and place them in the lib directory of your module
  5. Remove the generated source code and compiled classes

The MoleculePlugin create the jars so that you can use the boilerplate code without having to recompile any generated boilerplate code each time you recompile your project. In our demo example two jars are created:

4. Use Molecule

The MoleculePlugin has now created all the necessary boilerplate code so that we can start using Molecule. We need to import our newly created meta DSL and the Molecule api. Molecule uses implicit macro methods to transform our custom molecules to Datomic queries and we want to have as few of those in scope in order to compile as fast as possible. So we therefore try to import only the implicits we need. If you for instance are building molecules with maximum 10 attributes, you can therefore import the out10 (“out” for outputs):

import app.dsl.yourDomain._
import molecule.api.out10._ // (or other arity)

If you use input molecules awaiting an input then you can add inX where X is how many inputs (1, 2 or 3) you will use, for instance:

import molecule.api.in2_out10._

5. Connecting to the database

To make queries we need an implicit database connection. When initially developing our project we might make frequent changes to our schema and therefore recreate our database on each use (later, when our schema stabilizes we can retrieve the connection without recreating the database). The Molecule sbt-plugin creates a Schema transaction file that we can use now to transact our schema - simply by applying it to the recreateDbFrom method.

We assign the returned Connection to an implicit variable to have it available for our molecules to access the connection implicitly.

implicit val conn = recreateDbFrom(app.schema.YourDomainSchema)

// Save data
Person.name("John").age(26).gender("male").save // implicitly uses `conn`

// Get data
val (person, age, gender) = Person.name.age.gender.get.head

Next

See how we can model our domain with attributes in Schema