https://github.com/strojure/fitter
System component management library for Clojure.
https://github.com/strojure/fitter
clojure components dependency-injection inversion-of-control system-automation
Last synced: 5 months ago
JSON representation
System component management library for Clojure.
- Host: GitHub
- URL: https://github.com/strojure/fitter
- Owner: strojure
- License: unlicense
- Created: 2022-05-09T07:44:30.000Z (over 3 years ago)
- Default Branch: default
- Last Pushed: 2023-03-08T18:19:32.000Z (almost 3 years ago)
- Last Synced: 2024-12-03T16:42:14.960Z (about 1 year ago)
- Topics: clojure, components, dependency-injection, inversion-of-control, system-automation
- Language: Clojure
- Homepage:
- Size: 93.8 KB
- Stars: 25
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
# fitter
System component management library for Clojure.
[](https://clojars.org/com.github.strojure/fitter)
[](https://cljdoc.org/d/com.github.strojure/fitter)
[](https://github.com/strojure/fitter/actions/workflows/tests.yml)
Similar purpose libraries:
* [Component](https://github.com/stuartsierra/component)
* [Integrant](https://github.com/weavejester/integrant)
* [mount](https://github.com/tolitius/mount)
* [Dependency injection](https://github.com/darkleaf/di)
## Design goals
* 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.
## Terminology
* **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.
## Features
* 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).
## Basic usage
### Component declaration
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.
[system-test]: test/strojure/fitter/system_test.clj
Complete component defines its start, stop and suspend behaviour.
```clojure
(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
System state is a variable holding instances of the running components.
The state is initialized by `init` and then altered by `start!` and `stop!`.
```clojure
(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)))
```
## Examples
* [Feature: mount](doc/example/feature_mount.clj)
* [Feature: parallel](doc/example/feature_parallel.clj)
* [Feature: suspend](doc/example/feature_suspend.clj)
* [How-to: add logging](doc/example/how_to_add_logging.clj)
* [How-to: reuse components](doc/example/how_to_reuse_components.clj)
* [How-to: validate spec](doc/example/how_to_validate_spec.clj)