Datomic

Datomic #

After my post here in regard to create a micro service for books, in clojure using Pedestal framework, I want to move my books catalog to a database. I will take the chance to explore Datomic. Here I intend to document how I have installed it. Later I want to move books info to a database.

Datomic is a database engine created by the team responsible for clojure language. It brings the idea of immutability to databases borrowing the concept of atoms from clojure language where each operation on the database creates a new fact and everything is stored according to its timeline. I am used to have relational databases or even NoSQL databases where when I want to update a value, the new value is stored over the old one, loosing the history of that information.

For what I understand with Datomic when we update something we are creating a new atom (fact) with the new value and a timestamp for when the operation as occurred, leaving the previous value intact.

For example, imagine I have a bank account with :id and :balance properties. When I create this account I could have:

[
    {:id "1" :balance 0 :timestamp 2143464565}
]

Then I do a credit to my account and update the balance to 1000. It will create a new atom keeping the old one:

[
    {:id "1" :balance 0 :timestamp 1608707546927}
    {:id "1" :balance 1000 :timestamp 1608707570104}
]

It sounds interesting for event driven services. Here we can find more about Datomic.

Another interesting thing is, Datomic uses existing storage systems to do it works, as DynamoDB from Amazon, Oracle and Filesystem.

Installing Datomic #

To install Datomic we need to create a new account here and Download the Starter version here. After creating our account the easier way to find the file to download is here.

A starter license key will be sent through email.

Unpackage .zip in a folder you know, for later developments. To start a memory database engine, at the unpackaged folder execute:

./bin/run -m datomic.peer-server -h localhost -p 8998 -a myaccesskey,mysecret -d books,datomic:mem://books

myaccesskey and mysecret should be changed to be used further to access database as credentials.

Now I am going to add datomic client dependency on a project I have:

[com.datomic/client-pro "0.9.63"]

Now I use REPL just to start playing with it:

lein repl

Import datamoic Client:

(require '[datomic.client.api :as d])

Define configs to access peer server, the same when for engine started:

(def cfg {:server-type :peer-server
                 :access-key "myaccesskey"
                 :secret "mysecret"
                 :endpoint "localhost:8998"
                 :validate-hostnames false})

And connect to database

(def client (d/client cfg))
(def conn (d/connect client {:db-name "books"}))

Create schema #

"One of the great things about Datomic is that everything is data, including the definition of the for your . Because it is just data, it can be created and manipulated just like any other data you put into the database. To add new schema, you issue a that carries the new schema definition."

We need to create our schema. In Datomic we havef Entities which are composed by Àttributes:
Entities -> Attributes

Book is our Entity and our Attributes are title and author

(def book-schema [{ :db/ident :book/title :db/valueType :db.type/string :db/cardinality :db.cardinality/one :db/doc "Book title"} { :db/ident :book/author :db/valueType :db.type/string :db/cardinality :db.cardinality/one :db/doc "Book author"}])

To define our schema we need to create a transaction:

(d/transact conn {:tx-data book-schema})

Add books #

(def books [{:book/title "Clean code" :book/author "Uncle Book"} {:book/title "The Pragmatic Programmer" :book/author "Andy Hunt and Dave Thomas"}])

We just do another transaction, but now with our books.

(d/transact conn {:tx-data books})

Datalog to query #

Now we are able to query our database. First we need to have db instance:

(def db (d/db conn))

Here I am defining a query to access all books, return its id and title :

(def all-books-q '[:find ?e ?title :where [?e :book/title] [?e :book/title ?title]])

And now execute it:

(d/q all-titles-q db)

It will return something like:

[[17592186045418 "Clean code"] [17592186045419 "The Pragmatic Programmer"]]

I will explore queries in more details later, when I add the databae in the curret books api, built here.

Here we find more information about Datomic queries.

Published