This episode is for subscribers only. Sign Up or Log in to watch it.

39. Integrant

Published 15 March 18

Integrant is a “micro-framework for data-driven architecture”. It manages the lifecycle of your application, and the dependencies between components, similar to Component or Mount, but with some very different design decisions.

Integrant forms the foundation for Duct, a Clojure web framework by James Reeves, aka Weavejester.

Show notes

Integrant describes itself as a “micro-framework for data-driven architecture”. It’s a library that lets you split your app into parts, and manage their interdependencies and start/stop life cycle.

Integrant was created by James Reeves, a prolific Clojure programmer known online as Weavejester. It occupies the same space as Stuart Sierra’s Component library, which we’ve covered in detail in episode 26 and 31, but makes some very different design decisions.

Integrant forms the basis for Duct, a highly modular Clojure Web Framework. If you’re interested in learning Duct then you should get a good grip on Integrant first.

To demonstrate Integrant we’ll take a very basic web app, and look at different ways to approach its implementation. The app implements a HTTP single endpoint, which responds with the current date and time.

This episode will be self-contained, if you just follow along you should have everything you need. I will link to a github repo with the resulting code in the show notes, in case you want to have a look at the final result.

$ curl localhost:3000
It's Thu Mar 08 16:41:59 CST 2018

I’m using the Clojure CLI tools that came out together with Clojure 1.9. This deps.edn file contains all dependencies used in this episode.

browse source code

The master branch contains all code shown in the episode. If you want to follow along with the episode then you don’t need any code to get started, all you need are the Clojure CLI tools (tools.deps).

If you’re using an nREPL based environment that doesn’t yet have support for tools.deps, then check out the repo at tag ep39-start, and launch nREPL with clj -Anrepl.

Cheat sheet


;; lifecycle functions in integrant.core
(init config)
(halt! system)
(suspend! system)
(resume config system)
(prep config)

;; accompanying multimethods
(defn init-key :your/key [key value]
  ;; return implementation

(defn halt-key! :your/key [key implementation]
  ;; stop component, return value is ignored

(defn suspend-key! :your/key [key implementation]
  ;; pause component, return value is ignored

(defn resume-key :your/key [key config old-config implementation]
  ;; resume implementation, must return implementation

(defn prep-key :your/key [key config]
  ;; return updated config

;; other useful functions in integrant.core
(read-string "...edn...")   ;; like clojure.core/read-string, but knows how to read #ig/ref
(load-namespaces config)    ;; Tries loading namespaces based on config map keys
(find-derived config key)   ;; Find any keys in config (map) that derive from the given key

;; not mentioned in the episode, but still useful
(defmethod pre-init-spec :your/key [key]
  ;; return a clojure.spec that can validate a key's configuration

(fold system-map f v)       ;; similar to `clojure.core/reduce`, but folds over a config/system map in dependency order