Post on 28-Jul-2015
Components!Taming Clojure applications
David Dossot
Every application, ever*
● Subsystem lifecycle
● Long running processes
● Connections pools
● Caches
* almost
The Clojure Curse*
* http://www.winestockwebdesign.com/Essays/Lisp_Curse.html
● Clojure can do anything
● No official app framework
● NIH is a risk
● We went there...
Take #1 ~ Atoms ?
(def ^:dynamic server (atom nil))
(defn start [config]
(reset! server
(make-server config)))
(defn stop []
(.stop @server))
Take #1 ~ Atoms ✘
● Easy :)
● Shared state :(
● All over the place :(
● Reload unfriendly :(
● No dependencies :(
Take #2 ~ Maps ?
(defn start [execution-context config]
(assoc execution-context
:server (make-server config)))
(defn stop [execution-context]
(.stop (:server execution-context))
(dissoc execution-context :server))
Take #2 ~ Maps ✘
● Pure functions :)
● Reload friendly :)
● All in one place :)
● Ad-hoc lifecycle :(
● No dependencies :(
Take #3 ~ Components ?
(defrecord Server [server config]
component/Lifecycle
(start [this]
(if server ; already started
this
(assoc this :server (make-server config))))
(stop [this]
(if-not server ; already stopped
this
(do
(.stop server)
(assoc this :server nil)))))
Take #3 ~ Components ✔
● Pure functions :)
● Reload friendly :)
● All in one place :)
● Lifecycle API :)
● Dependencies :)
Caveat emptor
● Invasive● All fns that reify the module's behaviour
● Wish clj had Erlang's parameterized modules*
● Records can be weird● lein clean is your friend
* experimental feature, now removed :(
Resources
https://github.com/stuartsierra/component
https://youtu.be/13cmHf_kt-Q