https://github.com/danielsz/kampbell.git
git clone 'https://github.com/danielsz/kampbell.git'
(ql:quickload :danielsz.kampbell)
** Entity layer for key-value stores
Kampbell is designed to persist entities on top of key-value stores. By entities we mean the units of a domain model in an application: the user, the product, the order, etc. Think of an entity as a row in a table.
The aim is avoiding the cognitive overhead of database integration in the early stages of application development, and yet be able to persist data.
Just plain ol’ data and the joy of manipulating it with functions from the core library.
Kampbell has the potential to be a generic interface for multiple key-value store implementation, but currently consists of a single implementation on top of Konserve. Konserve is key-value database protocol that leverages the strengths of Clojure to offer a most compelling [[https://github.com/replikativ/konserve#features][value proposition]].
The public API of Kampbell is compatible with Clojure/Clojurescript. Some additional functionality is currently Clojure-only.
** Concepts
In Kampbell, a collection (of entities) is structured as a vector of maps. In the underlying key-value store, keys are names for the collection of entities (eg. users, products, orders), and values are the entities themselves (serialized vector of maps).
* Installation [[https://clojars.org/org.danielsz/kampbell/latest-version.svg]] * Usage
The public API consists of the four operations: ~get-entity~, ~save-entity~, ~update-entity~ and ~delete-entity~. Additionally, to get the entire collection, use ~get-entities~.
The [[https://github.com/danielsz/kampbell/blob/master/test/kampbell/core_test.clj][test]] included in the current repository contains a basic implementation that demonstrate the capabilities of Kampbell.
Here are our specs.
(s/def :domain.user/name string?) (s/def :domain.user/email (s/and string? #(re-matches #“^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,63}” %))) (s/def :domain/user (s/keys :req [:domain.user/name :domain.user/email]))
Here is a reference implementation.
(defn save-user [db v] (let [v (assoc v :domain.utils/created-at (Instant/now))] (<!! (k/save-entity db :domain/user v))))
(defn get-users [db] (<!! (k/get-entities db “users”)))
(defn get-user [db v] (<!! (k/get-entity db “users” :domain.user/email v)))
(defn update-user [db v & specs] {:pre [(some? specs) (every? keyword? specs)]} (<!! (k/update-entity db “users” v (into #{} specs))))
(defn delete-user [db v] (<!! (k/delete-entity db :domain/user v)))
** Benchmarks
The design goal of Kampbell is rapid prototyping, not performance. Optimizations are possible, but not a priority at this point. Because persistence is file-based, Kampbell will run circles around systems that persist data over the wire. Up to a certain a volume, that is. If you’re interested in contributing your own benchmarks, we’ll be happy to publish or link to yours.
** Advanced
When you define the persistence layer in an application as an interface, you gain the freedom of swapping the particular implementation at any time.
(defprotocol MyApplication “Persistence protocol for the application” (get-users [db]) (get-user [db v]) (save-user [db v]) (update-user [db v specs]) (delete-user [db v])