System Architecture
Data-driven config, multimethods (popular with Kit/Duct) Records implementing Lifecycle (start/stop), explicit dependencies
Global states (defstate) managed by the namespace dependency graph Data-driven, newer
Architecture patterns: a functional core with an imperative shell, data-oriented design, dependency injection via the system map, and configuration with Aero.
;; Integrant: describe the system as data, implement init/halt for each component
(require '[integrant.core :as ig])
(def config
{:db/pool {:url "jdbc:sqlite:app.db"}
:app/handler {:db (ig/ref :db/pool)}
:http/server {:port 3000 :handler (ig/ref :app/handler)}})
(defmethod ig/init-key :db/pool [_ {:keys [url]}]
(next.jdbc/get-datasource url))
(defmethod ig/init-key :app/handler [_ {:keys [db]}]
(fn [req] {:status 200 :body (str "DB ready: " (some? db))}))
(defmethod ig/init-key :http/server [_ {:keys [port handler]}]
(ring.adapter.jetty/run-jetty handler {:port port :join? false}))
(defmethod ig/halt-key! :http/server [_ server]
(.stop server))
(def system (ig/init config)) ; starts components in dependency order
;; (ig/halt! system) ; stops them in reverse order
;; integrant-repl: (go) (reset) (halt) for fast REPL restarts