Clojure REPL Driven Development

Building a Toy Digital Bank with Emacs, Mount, Pedestal and Datomic - Part 1

Posted by Promesante on April 28, 2021 · 9 mins read

Clojure, as programming language, has notably powerful features.

However, they are far from a random list, but make a very powerful and cohesive set, due to the synergy between them: “interaction or cooperation giving rise to a whole that is greater than the simple sum of its parts”.

There are plenty of resources available in the Web exposing them, i.e.:

Among them, we will take as reference Clojure Rationale, which enumerates:

The best way to realize the power of this synergy is actually putting those features into practice. And precisely that is actually the purpose of this post series:

Getting hands-on experience in Clojure REPL driven development (RDD)


Reloaded Workflow

Regarding RDD, one problem of Clojure is the time demanded for the REPL to get started.

It is due partially to time demanded by the JVM, but partially to Clojure itself as well. This time, and just having to restart the REPL, is incompatible with the “flow” you get working with RDD.

Therefore, several “reloaded workflows” have sprung out in Clojure ecosystem in order to fix this situation, giving us the chance to restart just the application under development, instead of the REPL. They use to complement “state managers”, meaning by “state” how the components required by our application are loaded. The “classic” on these subjects is Stuart Sierra’s Clojure Workflow Reloaded.

The most widely adopted state managers / reloaded workflows in Clojure ecosystem are:

One of the best articles I happened to read on RDD, and on reloaded workflows in particular, is the Guide to the Duct Framework, which shows a demo of a reloaded workflow in a strictly practical fashion, building a REST API from the ground up, and showing step-by-step the whole path. This approach is particularly illuminating for getting hands-on experience in RDD.

A project I’ve worked on was based on Luminus, a well known project template in the Clojure ecosystem, which tidily packages and integrates a vast set of tools widely adopted in the Clojure ecosystem. As its state manager, Luminus chose mount, and from then on I have adopted it as well.

The best tutorial explaining an example project on mount I have been able to find out so far is the official tutorial and example supplied with it.

In order to get a tutorial like that Duct’s Guide, but for a project based on mount instead of integrant, I have built an API similar to the one shown in mount’s tutorial and example: the accounts API of a toy digital bank. And this post will show how its implementation evolved, step by step, in a typical RDD way.

Instead of explaining each of those steps here, in the post series, we have kept track of them by means of git branches and pull requests, and then depict here just what, in our opinion, wouldn’t be clear enough by just taking a look at each of those PRs.

In contrast with that mount example, this post:

  • is based on Deps and CLI Tools instead of Leiningen
  • deals with a particular IDE, Emacs, because one of its most outstanding features is its natural integration with any Lisp dialect’s REPL, what is in turn a basic premise of the main subject of this article: RDD
  • adopts as Web framework Pedestal instead of Compojure

Endpoints

We will implement the following ones:

  1. account view, GET /accounts/:account-id: details of the single account with the account-id set
  2. transaction list, GET /accounts/:account-id/transactions: list of transactions already performed on the account with the account-id set
  3. transaction create, POST /accounts/:account-id: creating (executing) a transaction on the account with the account-id set;

Transactions

We will implement de following transactions:


Deposit

{
  "amount": 1000.00,
  "description": "appartment rent - march 2021"
}

Positive amount, no account (attribute exclusive for transfers)


Withdrawal

{
  "amount": -1000.00,
  "description": "appartment rent - march 2021"
}

Negative amount


Transfer

{
  "amount": -1000.00,
  "account": "account-1",
  "description": "appartment rent - march 2021"
}

Negative amount, setting target account’s id in account


Application Structure

Each of those endpoints will be implemented with the following two modules:

  1. db: Datomic database management
  2. web: REST API; mainly, Pedestal interceptors

Implementation Strategy and Post Series Structure

The aspects exposed in the previous two sections, Endpoints, and Application Structure, will determine this post series’ structure, as well as the implementation path exposed below, along pull request sequence, in the following iterative approch:

  1. account view: part 2 of this series
    1. database
    2. web
    3. end-to-end testing
    4. RDD session demo: part 3
  2. transaction list: part 4
    1. database
    2. web
    3. end-to-end testing
  3. transaction create: part 5
    1. database
    2. web
    3. end-to-end testing

Among these parts of the series, the most important one is part 3, 1.4, RDD session demo, as it actually fulfills the most the whole series goal: getting hands-on experience in Clojure REPL driven development (RDD).

But before beginning those steps, let’s tackle initial project setup which, due to its very own nature, is not reflected in any PR.


Initial Project Setup

We will use clj-new.

We need to add the following alias inside your :aliases map in ~/.clojure/deps.edn:

    ;; add this inside your :aliases map:
    :new {:extra-deps {com.github.seancorfield/clj-new
                         {:mvn/version "1.1.297"}}
            :exec-fn clj-new/create
            :exec-args {:template "app"}}

Then, let’s create the project:

$ clojure -X:new :name accounts/accounts

We are thus ready to begin coding.


References

We will take as reference the following articles:

Each of them has been an excellent tutorial for me, for its corresponding tool above. Hence, if you don’t have experience in any of them, I’d suggest to read them before going on with this series, as we assume that level of understanding about each.


Next Step

Hence, we are now ready to begin coding !

Come, join us to next part , where we will implement account view endpoint !

Till then !