System component management library for Clojure.
Similar purpose libraries:
- Describe system component dependencies in single place.
- Declare system components as easy as new function.
- Allow ad-hoc and dynamic dependencies defined by components themselves.
- Fit for systems with any amount of components.
- Maximum flexibility for any scenario.
- Help developer to reason about the whole system state.
- instance
- something useful instantiated :-)
- component
- the description how to initialize/destroy instance.
- registry
- map of keys and components used in system start/stop operations.
- system
- collection of dependent component instances.
- Start/stop system of components, all registered components or only part of them.
- Suspend/resume components on system restart.
- Bind component instances to global vars aka mount (optional).
- Parallel execution of components during system start/stop (optional).
Minimal component is just a function receiving system “map” of other component
instances and returning this component instance. The system “map” supports only
ILookup
interface, see system-test. All lookups instantiate requested
components and form a dependency between components dynamically. (!) All
components are tightly coupled together by system key names.
Complete component defines its start, stop and suspend behaviour.
(ns readme.component
(:require [strojure.fitter.component :as component]))
(def function-component
"Simple function describes component start behaviour."
(fn [{:keys [another-component]}]
(comment "Use" another-component)
:instance))
(def constant-component
"Just constant component."
(constantly true))
(def map-component
"Component described as hash map with required `::component/start` key."
{::component/start (constantly :instance)
::component/stop! (fn stop! [instance]
(comment "Destroy" instance))
::component/suspend! (fn suspend! [old-instance old-system]
(comment "Suspend" old-instance old-system)
(fn resume [new-system]
(comment "Resume" old-instance new-system)
:instance))})
(def assembled-component
"Same map as above created using `component/of`."
(component/of (constantly :instance)
(fn stop! [instance]
(comment "Destroy" instance))
(fn suspend! [old-instance old-system]
(comment "Suspend" old-instance old-system)
(fn resume [new-system]
(comment "Resume" old-instance new-system)
:instance))))
System state is a variable holding instances of the running components.
The state is initialized by init
and then altered by start!
and stop!
.
(ns readme.system-state
(:require [strojure.fitter.system :as system]))
(def ^:private registry
{::a (constantly ::a)
::b (fn [{::keys [a]}] {::b a})})
;; Initialize system state.
(defonce ^:private system!
(system/init {:registry registry}))
;; Start all system keys.
(system/start! system!)
;; Stop all running keys.
(system/stop! system!)
;; The `start!`, `stop!` and `deref` return the actual system map.
(let [{::keys [a b]} (system/start! system!)]
(comment "Work with" a b))
(let [_ (system/start! system!)
{::keys [a b]} (deref system!)]
(comment "Work with" a b))
;; Start/stop only specific keys.
(system/start! system! {:filter-keys #{::a}})
(system/stop! system! {:filter-keys #{::a}})
;; Start/stop system incrementally.
(doto system! (system/start! {:filter-keys #{:a}})
(system/start! {:filter-keys (complement #{:a})}))
(doto system! (system/stop! {:filter-keys (complement #{:a})})
(system/stop! {:filter-keys #{:a}}))
;; Update registry on start.
(system/start! system! {:registry (assoc registry ::c (constantly ::c))})
;; Suspend suspendable components on stop and resume them on start.
(doto system! (system/stop! {:suspend true})
(system/start!))
;; Execute components in parallel
(doto (system/init {:parallel true}) (system/start!)
(system/stop!))
(system/start! system! {:parallel true})
(system/stop! system! {:parallel true})
;; Use `with-open` to stop system automatically.
(with-open [s! (system/init {:registry registry})]
(let [{::keys [a b]} (system/start! s!)]
(comment "Work with" a b)))