{"id":13491329,"url":"https://github.com/strojure/fitter","last_synced_at":"2025-08-15T13:32:48.177Z","repository":{"id":80414043,"uuid":"490183372","full_name":"strojure/fitter","owner":"strojure","description":"System component management library for Clojure.","archived":false,"fork":false,"pushed_at":"2023-03-08T18:19:32.000Z","size":96,"stargazers_count":25,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"default","last_synced_at":"2024-12-03T16:42:14.960Z","etag":null,"topics":["clojure","components","dependency-injection","inversion-of-control","system-automation"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/strojure.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-09T07:44:30.000Z","updated_at":"2024-01-25T21:36:43.000Z","dependencies_parsed_at":null,"dependency_job_id":"ec779878-59a4-476a-a339-d781688d7e00","html_url":"https://github.com/strojure/fitter","commit_stats":{"total_commits":90,"total_committers":3,"mean_commits":30.0,"dds":0.05555555555555558,"last_synced_commit":"0dd8518b69d760e0d457c56e6fcc461da5fd7200"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strojure%2Ffitter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strojure%2Ffitter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strojure%2Ffitter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/strojure%2Ffitter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/strojure","download_url":"https://codeload.github.com/strojure/fitter/tar.gz/refs/heads/default","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229916371,"owners_count":18144129,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","components","dependency-injection","inversion-of-control","system-automation"],"created_at":"2024-07-31T19:00:55.754Z","updated_at":"2024-12-16T06:19:54.189Z","avatar_url":"https://github.com/strojure.png","language":"Clojure","funding_links":[],"categories":["Clojure"],"sub_categories":[],"readme":"# fitter\n\nSystem component management library for Clojure.\n\n[![Clojars Project](https://img.shields.io/clojars/v/com.github.strojure/fitter.svg)](https://clojars.org/com.github.strojure/fitter)\n\n[![cljdoc badge](https://cljdoc.org/badge/com.github.strojure/fitter)](https://cljdoc.org/d/com.github.strojure/fitter)\n[![tests](https://github.com/strojure/fitter/actions/workflows/tests.yml/badge.svg)](https://github.com/strojure/fitter/actions/workflows/tests.yml)\n\nSimilar purpose libraries:\n\n* [Component](https://github.com/stuartsierra/component)\n* [Integrant](https://github.com/weavejester/integrant)\n* [mount](https://github.com/tolitius/mount)\n* [Dependency injection](https://github.com/darkleaf/di)\n\n## Design goals\n\n* Describe system component dependencies in single place.\n* Declare system components as easy as new function.\n* Allow ad-hoc and dynamic dependencies defined by components themselves.\n* Fit for systems with any amount of components.\n* Maximum flexibility for any scenario.\n* Help developer to reason about the whole system state.\n\n## Terminology\n\n* **instance**\n    * something useful instantiated :-)\n* **component**\n    * the description how to initialize/destroy instance.\n* **registry**\n    * map of keys and components used in system start/stop operations.\n* **system**\n    * collection of dependent component instances.\n\n## Features\n\n* Start/stop system of components, all registered components or only part of\n  them.\n* Suspend/resume components on system restart.\n* Bind component instances to global vars aka mount (optional).\n* Parallel execution of components during system start/stop (optional).\n\n## Basic usage\n\n### Component declaration\n\nMinimal component is just a function receiving system “map” of other component\ninstances and returning this component instance. The system “map” supports only\n`ILookup` interface, see [system-test]. All lookups instantiate requested\ncomponents and form a dependency between components dynamically. (!) All\ncomponents are tightly coupled together by system key names.\n\n[system-test]: test/strojure/fitter/system_test.clj\n\nComplete component defines its start, stop and suspend behaviour.\n\n```clojure\n(ns readme.component\n  (:require [strojure.fitter.component :as component]))\n\n(def function-component\n  \"Simple function describes component start behaviour.\"\n  (fn [{:keys [another-component]}]\n    (comment \"Use\" another-component)\n    :instance))\n\n(def constant-component\n  \"Just constant component.\"\n  (constantly true))\n\n(def map-component\n  \"Component described as hash map with required `::component/start` key.\"\n  {::component/start (constantly :instance)\n   ::component/stop! (fn stop! [instance]\n                       (comment \"Destroy\" instance))\n   ::component/suspend! (fn suspend! [old-instance old-system]\n                          (comment \"Suspend\" old-instance old-system)\n                          (fn resume [new-system]\n                            (comment \"Resume\" old-instance new-system)\n                            :instance))})\n\n(def assembled-component\n  \"Same map as above created using `component/of`.\"\n  (component/of (constantly :instance)\n                (fn stop! [instance]\n                  (comment \"Destroy\" instance))\n                (fn suspend! [old-instance old-system]\n                  (comment \"Suspend\" old-instance old-system)\n                  (fn resume [new-system]\n                    (comment \"Resume\" old-instance new-system)\n                    :instance))))\n```\n\n### System state\n\nSystem state is a variable holding instances of the running components.\nThe state is initialized by `init` and then altered by `start!` and `stop!`.\n\n```clojure\n(ns readme.system-state\n  (:require [strojure.fitter.system :as system]))\n\n(def ^:private registry\n  {::a (constantly ::a)\n   ::b (fn [{::keys [a]}] {::b a})})\n\n;; Initialize system state.\n(defonce ^:private system!\n  (system/init {:registry registry}))\n\n;; Start all system keys.\n(system/start! system!)\n\n;; Stop all running keys.\n(system/stop! system!)\n\n;; The `start!`, `stop!` and `deref` return the actual system map. \n(let [{::keys [a b]} (system/start! system!)]\n  (comment \"Work with\" a b))\n(let [_ (system/start! system!)\n      {::keys [a b]} (deref system!)]\n  (comment \"Work with\" a b))\n\n;; Start/stop only specific keys.\n(system/start! system! {:filter-keys #{::a}})\n(system/stop! system! {:filter-keys #{::a}})\n\n;; Start/stop system incrementally.\n(doto system! (system/start! {:filter-keys #{:a}})\n              (system/start! {:filter-keys (complement #{:a})}))\n(doto system! (system/stop! {:filter-keys (complement #{:a})})\n              (system/stop! {:filter-keys #{:a}}))\n\n;; Update registry on start.\n(system/start! system! {:registry (assoc registry ::c (constantly ::c))})\n\n;; Suspend suspendable components on stop and resume them on start.\n(doto system! (system/stop! {:suspend true})\n              (system/start!))\n\n;; Execute components in parallel\n(doto (system/init {:parallel true}) (system/start!)\n                                     (system/stop!))\n(system/start! system! {:parallel true})\n(system/stop! system! {:parallel true})\n\n;; Use `with-open` to stop system automatically.\n(with-open [s! (system/init {:registry registry})]\n  (let [{::keys [a b]} (system/start! s!)]\n    (comment \"Work with\" a b)))\n```\n\n## Examples\n\n* [Feature: mount](doc/example/feature_mount.clj)\n* [Feature: parallel](doc/example/feature_parallel.clj)\n* [Feature: suspend](doc/example/feature_suspend.clj)\n* [How-to: add logging](doc/example/how_to_add_logging.clj)\n* [How-to: reuse components](doc/example/how_to_reuse_components.clj)\n* [How-to: validate spec](doc/example/how_to_validate_spec.clj)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrojure%2Ffitter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstrojure%2Ffitter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrojure%2Ffitter/lists"}