Architecture

Stack

crud is written in Clojure, a functional and dynamically typed language. It was choosen because of its philosophy around data manipulation (highlights: dynamic typing, simple data structures, nil-punning) and its great developer experience.


To persist the data crud connects to a MongoDB. Its hands-off approach to the structuring of data made it a great fit with what crud is trying to provide. Besides MongoDB, there is also a second implementation of the persistence layer which uses Clojure Atoms to store data in-memory.


Clojure being a JVM hosted language opens up a lot of options when it comes to hosting and http servers. As of right now Clojure's http-kit was choosen, as it was packaged with the Luminus framework. Though it makes sense to switch to Jetty once performance trumps development velocity in terms of priority.

Deployment

Currently there are many different ways to deploy crud. With Docker being the recommended way to self-host, as it's the quickest.


In the future an SQL implementation would be great as it would allow to embed a SQLite, thus providing persistence with improved protability.

Contributing

If you want to look at the code or have ideas on how to improve crud further feel free to checkout our repo.

Namespaces

The major namespaces the following:


Name Purpose
crud.logic.core Business Logic
crud.entrypoint.core Glue routes, wrappers and logic together
crud.persistence.core Protocol and implementation for persistence

Also important is the crud.config namespace which provides a summary of all configurations for easy consumption. The application itself is started in crud.core.

Layers

crud has the usual layers of a Web API:


Name Purpose
entrypoint.core Glue everything together
entrypoint.wrappers Setup, parse, reject request/response etc.
entrypoint.routes Route request to destination
logic.core Consum request
persistence.protocol Provide basic functions to access data

Data and errors

To take advantage of Clojure's nil-punning and the REPL-driven development everything returned by each layer is in form of a [data error] tuple. This both encapsulates the error- and succes-case. If an error occurs data will be nil and a :message key will be present inside of error. If the action was sucessful error will be nil.


This blog post talks more about the [data error] tuple.



Last Edited: 2023.01.09; crud:1.2.0