Why bother with Integrant?

The Clojure backends that I’ve worked on have often had a number of subcomponents: a HTTP server such as Jetty, a database connection pool, a scheduler, a message queue processor and so on. When you start the backend, you need to start all those components. You may have seen or written code like this to do so:


(defn start []
  (let [connection-pool (connection-pool/create ...)
        stop-scheduler (scheduler/create ...)
        ring-handler (create-handler connection-pool)
        stop-server (jetty/start ring-handler)]
    (fn []
      (stop-server)
      (stop-scheduler)
      (.stop connection-pool))))

This function starts a bunch of service components and returns a function that stops those components when called. As you probably know, the same problem can be solved with Integrant. This works, though, so why bother?

Correct start-up and shut-down order. In the example, to create the Ring handler, you need to first create a connection pool and then pass it to the handler. They should be closed in the opposite order. Integrant keeps track of this for you and automatically starts and stops everything in the correct order.

Starting a partial system. In the example above, if you only want to start the HTTP server but not the scheduler - maybe for development or tests - you need to create another function that takes care of that. In Integrant, all you need to do is to call init with a sequence of keywords specifying the components that you want to start.

Good for REPL-driven and test-driven workflows. If you run a REPL and your tests, or if you run your tests in parallel, in the same JVM, you’ll probably want to start separate instances of your service for each of those. On the other hand, for REPL-driven development, you’ll probably want to have a single global instance so that it is easy to poke. You can set it up by hand… or you can use Integrant and integrant-repl.

You do not need to use Integrant (or its alternatives like mount). It’s easy enough to write something passable by hand. But using it will cut down boilerplate, enable nice workflows, and probably squash some subtle bugs as well.


Comments or questions? Send me an e-mail.