Liking cljdoc? Tell your friends :D

patcho.lifecycle

Module lifecycle management with dependency resolution.

Provides runtime lifecycle management for modules with explicit dependency declaration and automatic startup ordering.

Design Philosophy

This namespace provides a lightweight registry for managing module lifecycle:

  • Modules register themselves at namespace load time
  • Dependencies are declared explicitly
  • Operations are RECURSIVE - automatically handle dependencies
  • Cleanup uses reference counting (claimed-by semantics)
  • State tracking prevents double-start/stop
  • Setup phase is separate from start/stop (one-time vs runtime)

Usage

Registration (at namespace load time)

(ns my.module
  (:require [patcho.lifecycle :as lifecycle]))

(lifecycle/register-module! :my/module
  {:depends-on [:other/module]
   :setup (fn []
            (create-tables!))
   :cleanup (fn []
              (drop-tables!))
   :start (fn []
            (start-connections!)
            (subscribe-to-events!))
   :stop (fn []
           (unsubscribe!)
           (close-connections!))})

Application Startup

(ns my.app
  (:require
    my.database      ; Registers :my/database
    my.cache         ; Registers :my/cache (depends on :my/database)
    my.api           ; Registers :my/api (depends on :my/cache)
    [patcho.lifecycle :as lifecycle]))

;; Set default store once (typically in main or init)
(lifecycle/set-store! (lifecycle/->FileLifecycleStore ".lifecycle"))

;; Simple approach: Just start! (auto-runs setup if needed)
(lifecycle/start! :my/api)
;; → Setups (if needed) and starts: :my/database → :my/cache → :my/api
;; Setup is idempotent - only runs once, tracked via lifecycle store

;; OR explicit control: Run setup separately (optional)
(lifecycle/setup! :my/api)  ; One-time: setup all dependencies
(lifecycle/start! :my/api)  ; Runtime: start all dependencies

;; Visualize dependencies
(lifecycle/print-dependency-tree :my/api)
;; :my/api
;; └── :my/cache
;;     └── :my/database

;; Later: stop (RECURSIVE - stops dependents first)
(lifecycle/stop! :my/database)
;; → Stops: :my/api → :my/cache → :my/database

;; Surgical stop (only this module)
(lifecycle/stop-only! :my/api)

;; Cleanup (RECURSIVE with reference checking)
(lifecycle/cleanup! :my/api)
;; → Cleans :my/api
;; → Cleans :my/cache (if no other modules depend on it)
;; → Cleans :my/database (if no other modules depend on it)

Relationship to patcho.patch

Lifecycle management is complementary to version management:

  • patcho.patch: Version migrations (data/schema state transitions)
  • patcho.lifecycle: Runtime management (start/stop with dependencies)

They work together:

  1. Apply patches to reach target version (patch/level!)
  2. Start runtime services (lifecycle/start!) - auto-runs setup if needed

Note: start! automatically runs setup for modules that have a setup function, making the system easy to use while maintaining explicit control when needed.

Design Decisions

  • Registry pattern: Modules stored in atom (like Patcho's patches)
  • Keyword topics: Same style as Patcho (:my/module)
  • Explicit dependencies: Clear, validated at runtime
  • RECURSIVE operations: start/setup/cleanup handle deps automatically
  • Auto-setup: start! runs setup if needed (idempotent via lifecycle store)
  • Reference counting: cleanup only removes if not claimed by others
  • State tracking: Prevents double-start bugs
  • Functions not protocols: Simple, direct
  • Minimal dependencies: Just clojure.core
Module lifecycle management with dependency resolution.

Provides runtime lifecycle management for modules with explicit dependency
declaration and automatic startup ordering.

## Design Philosophy

This namespace provides a lightweight registry for managing module lifecycle:
- Modules register themselves at namespace load time
- Dependencies are declared explicitly
- Operations are RECURSIVE - automatically handle dependencies
- Cleanup uses reference counting (claimed-by semantics)
- State tracking prevents double-start/stop
- Setup phase is separate from start/stop (one-time vs runtime)

## Usage

### Registration (at namespace load time)

    (ns my.module
      (:require [patcho.lifecycle :as lifecycle]))

    (lifecycle/register-module! :my/module
      {:depends-on [:other/module]
       :setup (fn []
                (create-tables!))
       :cleanup (fn []
                  (drop-tables!))
       :start (fn []
                (start-connections!)
                (subscribe-to-events!))
       :stop (fn []
               (unsubscribe!)
               (close-connections!))})

### Application Startup

    (ns my.app
      (:require
        my.database      ; Registers :my/database
        my.cache         ; Registers :my/cache (depends on :my/database)
        my.api           ; Registers :my/api (depends on :my/cache)
        [patcho.lifecycle :as lifecycle]))

    ;; Set default store once (typically in main or init)
    (lifecycle/set-store! (lifecycle/->FileLifecycleStore ".lifecycle"))

    ;; Simple approach: Just start! (auto-runs setup if needed)
    (lifecycle/start! :my/api)
    ;; → Setups (if needed) and starts: :my/database → :my/cache → :my/api
    ;; Setup is idempotent - only runs once, tracked via lifecycle store

    ;; OR explicit control: Run setup separately (optional)
    (lifecycle/setup! :my/api)  ; One-time: setup all dependencies
    (lifecycle/start! :my/api)  ; Runtime: start all dependencies

    ;; Visualize dependencies
    (lifecycle/print-dependency-tree :my/api)
    ;; :my/api
    ;; └── :my/cache
    ;;     └── :my/database

    ;; Later: stop (RECURSIVE - stops dependents first)
    (lifecycle/stop! :my/database)
    ;; → Stops: :my/api → :my/cache → :my/database

    ;; Surgical stop (only this module)
    (lifecycle/stop-only! :my/api)

    ;; Cleanup (RECURSIVE with reference checking)
    (lifecycle/cleanup! :my/api)
    ;; → Cleans :my/api
    ;; → Cleans :my/cache (if no other modules depend on it)
    ;; → Cleans :my/database (if no other modules depend on it)

## Relationship to patcho.patch

Lifecycle management is complementary to version management:
- patcho.patch: Version migrations (data/schema state transitions)
- patcho.lifecycle: Runtime management (start/stop with dependencies)

They work together:
1. Apply patches to reach target version (patch/level!)
2. Start runtime services (lifecycle/start!) - auto-runs setup if needed

Note: start! automatically runs setup for modules that have a setup function,
making the system easy to use while maintaining explicit control when needed.

## Design Decisions

- Registry pattern: Modules stored in atom (like Patcho's patches)
- Keyword topics: Same style as Patcho (:my/module)
- Explicit dependencies: Clear, validated at runtime
- RECURSIVE operations: start/setup/cleanup handle deps automatically
- Auto-setup: start! runs setup if needed (idempotent via lifecycle store)
- Reference counting: cleanup only removes if not claimed by others
- State tracking: Prevents double-start bugs
- Functions not protocols: Simple, direct
- Minimal dependencies: Just clojure.core
raw docstring

*lifecycle-store*clj

Dynamic var for the lifecycle store.

Defaults to AtomLifecycleStore (in-memory). State is lost on restart unless you explicitly set a persistent store (FileLifecycleStore, database, etc.).

Set globally via set-store!, or temporarily via with-store macro. State is automatically migrated when switching stores.

Dynamic var for the lifecycle store.

Defaults to AtomLifecycleStore (in-memory). State is lost on restart unless
you explicitly set a persistent store (FileLifecycleStore, database, etc.).

Set globally via set-store!, or temporarily via with-store macro.
State is automatically migrated when switching stores.
raw docstring

cleanup!clj

(cleanup! topic)

Run one-time cleanup for a module and all its dependants (modules that depend on it).

Cleanup order:

  1. First cleanup all dependants (modules that depend on this one) - recursively
  2. Then cleanup this module

This ensures no module is left in an inconsistent state with a missing dependency. Dependencies (modules this one depends on) are NOT cleaned - they may be shared.

This is idempotent - tracks if cleanup already ran via LifecycleStore.

Uses lifecycle-store - set it via set-store! or with-store macro.

Parameters: topic - Module keyword

Example: (set-store! (->FileLifecycleStore ".lifecycle")) (cleanup! :my/database) ; Also cleans up :my/cache if it depends on :my/database

Run one-time cleanup for a module and all its dependants (modules that depend on it).

Cleanup order:
1. First cleanup all dependants (modules that depend on this one) - recursively
2. Then cleanup this module

This ensures no module is left in an inconsistent state with a missing dependency.
Dependencies (modules this one depends on) are NOT cleaned - they may be shared.

This is idempotent - tracks if cleanup already ran via LifecycleStore.

Uses *lifecycle-store* - set it via set-store! or with-store macro.

Parameters:
  topic - Module keyword

Example:
  (set-store! (->FileLifecycleStore ".lifecycle"))
  (cleanup! :my/database)  ; Also cleans up :my/cache if it depends on :my/database
raw docstring

clear-errors!clj

(clear-errors!)

Clear all recorded module errors.

Useful after fixing issues and before retrying startup.

Example: (clear-errors!) (start! :my/module) ; Fresh start without old errors

Clear all recorded module errors.

Useful after fixing issues and before retrying startup.

Example:
  (clear-errors!)
  (start! :my/module)  ; Fresh start without old errors
raw docstring

dependency-graphclj

(dependency-graph)

Returns the dependency graph as a map.

Useful for visualization or debugging.

Returns: {topic {:depends-on [...] :dependents [...]}}

Example: (dependency-graph) ;; => {:my/database {:depends-on [] :dependents [:my/cache]} ;; :my/cache {:depends-on [:my/database] :dependents [:my/api]} ;; :my/api {:depends-on [:my/cache] :dependents []}}

Returns the dependency graph as a map.

Useful for visualization or debugging.

Returns:
  {topic {:depends-on [...] :dependents [...]}}

Example:
  (dependency-graph)
  ;; => {:my/database {:depends-on [] :dependents [:my/cache]}
  ;;     :my/cache {:depends-on [:my/database] :dependents [:my/api]}
  ;;     :my/api {:depends-on [:my/cache] :dependents []}}
raw docstring

dependency-tree-stringclj

(dependency-tree-string)
(dependency-tree-string topic)

Generate ASCII tree visualization of module dependencies.

With no arguments, shows all root modules (modules nothing depends on). With topic argument, shows dependency tree for that module.

Parameters: topic - (Optional) Module keyword to visualize

Returns: String with ASCII tree visualization

Example: (dependency-tree-string :my/api) ;; => ":my/api ;; ├── :my/cache ;; │ └── :my/database ;; └── :my/auth ;; └── :my/database"

Generate ASCII tree visualization of module dependencies.

With no arguments, shows all root modules (modules nothing depends on).
With topic argument, shows dependency tree for that module.

Parameters:
  topic - (Optional) Module keyword to visualize

Returns:
  String with ASCII tree visualization

Example:
  (dependency-tree-string :my/api)
  ;; => ":my/api
  ;;     ├── :my/cache
  ;;     │   └── :my/database
  ;;     └── :my/auth
  ;;         └── :my/database"
raw docstring

LifecycleStorecljprotocol

Persistent storage for lifecycle state tracking.

Tracks one-time operations (setup, cleanup) across process restarts. Runtime state (started?) is NOT persisted - it's in-memory only.

Persistent storage for lifecycle state tracking.

Tracks one-time operations (setup, cleanup) across process restarts.
Runtime state (started?) is NOT persisted - it's in-memory only.

read-lifecycle-stateclj

(read-lifecycle-state store topic)

Read persistent state for a topic. Returns map with :setup-complete?, :cleanup-complete?, etc.

Read persistent state for a topic.
Returns map with :setup-complete?, :cleanup-complete?, etc.

write-lifecycle-stateclj

(write-lifecycle-state store topic state)

Write persistent state for a topic. State is a map with :setup-complete?, :cleanup-complete?, etc.

Write persistent state for a topic.
State is a map with :setup-complete?, :cleanup-complete?, etc.
raw docstring

migrate-store!clj

(migrate-store! from-store to-store topics)

Migrate lifecycle state from one store to another.

Useful for bootstrapping: start with AtomLifecycleStore, setup database, then migrate state to database-backed store.

Args: from-store - Source LifecycleStore to read from to-store - Destination LifecycleStore to write to topics - Collection of topic keywords to migrate (or nil for all registered)

Returns: Set of migrated topics

Example: ;; Bootstrap with in-memory store (def bootstrap-store (->AtomLifecycleStore (atom {}))) (set-store! bootstrap-store) (setup! :synthigy/database)

;; Migrate to database store (migrate-store! bootstrap-store db/db nil) (set-store! db/db)

Migrate lifecycle state from one store to another.

Useful for bootstrapping: start with AtomLifecycleStore, setup database,
then migrate state to database-backed store.

Args:
  from-store - Source LifecycleStore to read from
  to-store   - Destination LifecycleStore to write to
  topics     - Collection of topic keywords to migrate (or nil for all registered)

Returns:
  Set of migrated topics

Example:
  ;; Bootstrap with in-memory store
  (def bootstrap-store (->AtomLifecycleStore (atom {})))
  (set-store! bootstrap-store)
  (setup! :synthigy/database)

  ;; Migrate to database store
  (migrate-store! bootstrap-store db/*db* nil)
  (set-store! db/*db*)
raw docstring

module-errorsclj

Registry of module errors. Structure: {topic {:error Exception :timestamp Date}}

Registry of module errors. Structure: {topic {:error Exception :timestamp Date}}
raw docstring

module-infoclj

(module-info topic)

Returns info map for a module, or nil if not registered.

If lifecycle-store is set, also includes :setup-complete? and :cleanup-complete?.

Returns: {:depends-on [...] :started? true/false :has-setup? true/false :has-cleanup? true/false :setup-complete? true/false ; Only if lifecycle-store is set :cleanup-complete? true/false} ; Only if lifecycle-store is set

Returns info map for a module, or nil if not registered.

If *lifecycle-store* is set, also includes :setup-complete? and :cleanup-complete?.

Returns:
  {:depends-on [...]
   :started? true/false
   :has-setup? true/false
   :has-cleanup? true/false
   :setup-complete? true/false    ; Only if *lifecycle-store* is set
   :cleanup-complete? true/false} ; Only if *lifecycle-store* is set
raw docstring

(print-dependency-tree)
(print-dependency-tree topic)

Print ASCII tree visualization of module dependencies.

With no arguments, shows all root modules. With topic argument, shows dependency tree for that module.

Parameters: topic - (Optional) Module keyword to visualize

Example: (print-dependency-tree :my/api) ;; Prints: ;; :my/api ;; ├── :my/cache ;; │ └── :my/database ;; └── :my/auth ;; └── :my/database

Print ASCII tree visualization of module dependencies.

With no arguments, shows all root modules.
With topic argument, shows dependency tree for that module.

Parameters:
  topic - (Optional) Module keyword to visualize

Example:
  (print-dependency-tree :my/api)
  ;; Prints:
  ;; :my/api
  ;; ├── :my/cache
  ;; │   └── :my/database
  ;; └── :my/auth
  ;;     └── :my/database
raw docstring

(print-system-report)

Print formatted system status report showing started/stopped modules and errors.

Uses markers: [OK] - Started modules [ ] - Stopped modules (ready to start) [!] - Modules with errors or missing dependencies

Example: (print-system-report) ;; === System Report === ;; Registered: 7 | Started: 3 | Stopped: 4 ;; ;; [OK] Started modules: ;; * :synthigy/transit ;; * :synthigy/database -> [:synthigy/transit] ;; ;; [ ] Stopped modules (ready to start): ;; * :synthigy/iam -> [:synthigy/dataset] ;; ;; [!] Modules with errors: ;; * :synthigy/admin -> [:synthigy/dataset] ;; ERROR at 2026-01-07 18:23:45 ;; Message: No such var: db/db ;; Data: {:module :synthigy/admin}

Print formatted system status report showing started/stopped modules and errors.

Uses markers:
  [OK] - Started modules
  [ ]  - Stopped modules (ready to start)
  [!]  - Modules with errors or missing dependencies

Example:
  (print-system-report)
  ;; === System Report ===
  ;; Registered: 7 | Started: 3 | Stopped: 4
  ;;
  ;; [OK] Started modules:
  ;;   * :synthigy/transit
  ;;   * :synthigy/database -> [:synthigy/transit]
  ;;
  ;; [ ] Stopped modules (ready to start):
  ;;   * :synthigy/iam -> [:synthigy/dataset]
  ;;
  ;; [!] Modules with errors:
  ;;   * :synthigy/admin -> [:synthigy/dataset]
  ;;     ERROR at 2026-01-07 18:23:45
  ;;     Message: No such var: db/*db*
  ;;     Data: {:module :synthigy/admin}
raw docstring

(print-topology-layers)

Print layered list visualization of all modules.

Shows modules grouped by dependency depth with explicit dependencies.

Example: (print-topology-layers) ;; Prints: ;; Layer 0 (foundation): ;; transit ;; ;; Layer 1: ;; database → [transit] ;; ;; Layer 2: ;; dataset → [database]

Print layered list visualization of all modules.

Shows modules grouped by dependency depth with explicit dependencies.

Example:
  (print-topology-layers)
  ;; Prints:
  ;; Layer 0 (foundation):
  ;;   transit
  ;;
  ;; Layer 1:
  ;;   database → [transit]
  ;;
  ;; Layer 2:
  ;;   dataset → [database]
raw docstring

register-module!clj

(register-module! topic
                  {:keys [depends-on setup cleanup start stop]
                   :or {stop (fn [] nil) start (fn [] nil)}})

Register a module with its lifecycle functions and dependencies.

This should be called at namespace load time (top-level form).

Parameters: topic - Keyword identifying the module (e.g., :synthigy/iam) spec - Map with keys: :depends-on - Vector of topic keywords this module depends on :setup - (Optional) fn taking no args for one-time setup :cleanup - (Optional) fn taking no args for one-time cleanup :start - fn taking no args to start runtime services :stop - fn taking no args to stop runtime services

Example: (register-module! :my/database {:start (fn [] (connect! db-config)) :stop (fn [] (disconnect!))})

(register-module! :my/cache {:depends-on [:my/database] :setup (fn [] (create-cache-tables!)) :cleanup (fn [] (drop-cache-tables!)) :start (fn [] (start-cache!)) :stop (fn [] (stop-cache!))})

Register a module with its lifecycle functions and dependencies.

This should be called at namespace load time (top-level form).

Parameters:
  topic   - Keyword identifying the module (e.g., :synthigy/iam)
  spec    - Map with keys:
            :depends-on   - Vector of topic keywords this module depends on
            :setup        - (Optional) fn taking no args for one-time setup
            :cleanup      - (Optional) fn taking no args for one-time cleanup
            :start        - fn taking no args to start runtime services
            :stop         - fn taking no args to stop runtime services

Example:
  (register-module! :my/database
    {:start (fn [] (connect! db-config))
     :stop (fn [] (disconnect!))})

  (register-module! :my/cache
    {:depends-on [:my/database]
     :setup (fn [] (create-cache-tables!))
     :cleanup (fn [] (drop-cache-tables!))
     :start (fn [] (start-cache!))
     :stop (fn [] (stop-cache!))})
raw docstring

registered-modulesclj

(registered-modules)

Returns vector of all registered module topics.

Returns vector of all registered module topics.
raw docstring

reset-registry!clj

(reset-registry!)

Clear all registered modules.

WARNING: This is primarily for testing. Using in production may leave modules in inconsistent state.

Stop your running modules first before calling this.

Clear all registered modules.

WARNING: This is primarily for testing. Using in production
may leave modules in inconsistent state.

Stop your running modules first before calling this.
raw docstring

reset-store!clj

(reset-store!)

Reset lifecycle store to a fresh in-memory AtomLifecycleStore.

Use this when cleaning up the database to clear all persisted state and start fresh. Does NOT migrate state (old state is discarded).

Example: ;; In database cleanup (reset-store!) ; Fresh atom store, no state

Reset lifecycle store to a fresh in-memory AtomLifecycleStore.

Use this when cleaning up the database to clear all persisted state
and start fresh. Does NOT migrate state (old state is discarded).

Example:
  ;; In database cleanup
  (reset-store!)  ; Fresh atom store, no state
raw docstring

restart!clj

(restart! topic)

Restart a module (stop-only then start).

Uses stop-only! (non-recursive) for surgical control. Useful for REPL development when code changes.

Parameters: topic - Module keyword

Example: (restart! :my/cache)

Restart a module (stop-only then start).

Uses stop-only! (non-recursive) for surgical control.
Useful for REPL development when code changes.

Parameters:
  topic - Module keyword

Example:
  (restart! :my/cache)
raw docstring

set-store!clj

(set-store! store)

Set the default lifecycle store globally.

Automatically migrates state from the previous store to the new one. This makes store transitions seamless (e.g., from bootstrap AtomLifecycleStore to database-backed store).

Arguments: store - Implementation of LifecycleStore protocol (or nil to clear)

Example: ;; Bootstrap with in-memory store (set-store! (->AtomLifecycleStore (atom {}))) (setup! :my/database)

;; Switch to DB - state auto-migrates (set-store! db/db)

(setup! :my/app) ; Uses database store

Set the default lifecycle store globally.

Automatically migrates state from the previous store to the new one.
This makes store transitions seamless (e.g., from bootstrap AtomLifecycleStore
to database-backed store).

 Arguments:
   store - Implementation of LifecycleStore protocol (or nil to clear)

 Example:
   ;; Bootstrap with in-memory store
   (set-store! (->AtomLifecycleStore (atom {})))
   (setup! :my/database)

   ;; Switch to DB - state auto-migrates
   (set-store! db/*db*)

   (setup! :my/app)     ; Uses database store
raw docstring

setup!clj

(setup! & topics)

Run one-time setup for one or more modules RECURSIVELY.

This is idempotent - tracks if setup already ran via LifecycleStore. STARTS dependencies first (setup often needs them running).

Uses lifecycle-store - set it via set-store! or with-store macro.

Parameters: topics - One or more module keywords

Example: (set-store! (->FileLifecycleStore ".lifecycle")) (setup! :my/cache) (setup! :my/db :my/cache :my/api)

Run one-time setup for one or more modules RECURSIVELY.

This is idempotent - tracks if setup already ran via LifecycleStore.
STARTS dependencies first (setup often needs them running).

Uses *lifecycle-store* - set it via set-store! or with-store macro.

Parameters:
  topics - One or more module keywords

Example:
  (set-store! (->FileLifecycleStore ".lifecycle"))
  (setup! :my/cache)
  (setup! :my/db :my/cache :my/api)
raw docstring

setup-complete?clj

(setup-complete? topic)

start!clj

(start! topic)
(start! topic & more-topics)

Start a module RECURSIVELY (starts all dependencies first).

This is idempotent - won't start if already started. Dependencies are started in correct order automatically. Records errors in module-errors atom if startup fails.

If module has a setup function and setup hasn't been completed yet, automatically runs setup first (idempotent via lifecycle store).

Parameters: topic - Module keyword

Example: (start! :my/api) ;; → Setups (if needed) and starts: :my/database → :my/cache → :my/api

Start a module RECURSIVELY (starts all dependencies first).

This is idempotent - won't start if already started.
Dependencies are started in correct order automatically.
Records errors in module-errors atom if startup fails.

If module has a setup function and setup hasn't been completed yet,
automatically runs setup first (idempotent via lifecycle store).

Parameters:
  topic - Module keyword

Example:
  (start! :my/api)
  ;; → Setups (if needed) and starts: :my/database → :my/cache → :my/api
raw docstring

started-modulesclj

(started-modules)

Returns vector of currently started module topics.

Returns vector of currently started module topics.
raw docstring

started?clj

(started? topic)

Returns true if module is currently started.

Returns true if module is currently started.
raw docstring

stop!clj

(stop! topic)
(stop! topic & more-topics)

Stop a module and all its dependents RECURSIVELY.

Stop order (dependents first):

  1. First stop all dependents (modules that depend on this one) - recursively
  2. Then stop this module

This ensures no module is left running without its dependencies. This is symmetric with start! (which starts dependencies first).

This is idempotent - won't stop if already stopped.

Parameters: topic - Module keyword

Example: (stop! :my/database) ;; → Stops: :my/api → :my/cache → :my/database (dependents first)

Stop a module and all its dependents RECURSIVELY.

Stop order (dependents first):
1. First stop all dependents (modules that depend on this one) - recursively
2. Then stop this module

This ensures no module is left running without its dependencies.
This is symmetric with start! (which starts dependencies first).

This is idempotent - won't stop if already stopped.

Parameters:
  topic - Module keyword

Example:
  (stop! :my/database)
  ;; → Stops: :my/api → :my/cache → :my/database (dependents first)
raw docstring

stop-only!clj

(stop-only! topic)

Stop a single module (NOT recursive - only stops this module).

This is idempotent - won't stop if already stopped. Use this for surgical control when you don't want to affect dependents.

Parameters: topic - Module keyword

Example: (stop-only! :my/api) ; Only stops :my/api, dependencies stay running

Stop a single module (NOT recursive - only stops this module).

This is idempotent - won't stop if already stopped.
Use this for surgical control when you don't want to affect dependents.

Parameters:
  topic - Module keyword

Example:
  (stop-only! :my/api)  ; Only stops :my/api, dependencies stay running
raw docstring

system-reportclj

(system-report)

Get comprehensive system status report including errors.

Returns map with: :registered - Total count of registered modules :started - Count of started modules :stopped - Count of stopped modules :modules - Map of {topic {:status :started/:stopped :depends-on [...] :dependents [...] :missing-dependencies [...] :error {:message "..." :timestamp Date :exception Exception}}}

Example: (system-report) ;=> {:registered 7 :started 3 :stopped 4 :modules {:synthigy/transit {:status :started ...} :synthigy/admin {:status :stopped :error {:message "No such var: db/db" :timestamp #inst "2026-01-07" :exception #<ExceptionInfo ...>}}}}}

Get comprehensive system status report including errors.

Returns map with:
  :registered - Total count of registered modules
  :started - Count of started modules
  :stopped - Count of stopped modules
  :modules - Map of {topic {:status :started/:stopped
                            :depends-on [...]
                            :dependents [...]
                            :missing-dependencies [...]
                            :error {:message "..."
                                    :timestamp Date
                                    :exception Exception}}}

Example:
  (system-report)
  ;=> {:registered 7
       :started 3
       :stopped 4
       :modules {:synthigy/transit {:status :started ...}
                 :synthigy/admin {:status :stopped
                                   :error {:message "No such var: db/*db*"
                                           :timestamp #inst "2026-01-07"
                                           :exception #<ExceptionInfo ...>}}}}}
raw docstring

topology-layers-stringclj

(topology-layers-string)

Generate layered list visualization of all modules.

Shows modules grouped by dependency depth with explicit dependencies.

Returns: String with layered list visualization

Example: (topology-layers-string) ;; => "Layer 0 (foundation): ;; :synthigy/transit ;; ;; Layer 1: ;; :synthigy/database → [:synthigy/transit] ;; ;; Layer 2: ;; :synthigy/dataset → [:synthigy/database] ;; ;; Layer 3: ;; :synthigy/iam → [:synthigy/dataset] ;; :synthigy/lacinia → [:synthigy/dataset]"

Generate layered list visualization of all modules.

Shows modules grouped by dependency depth with explicit dependencies.

Returns:
  String with layered list visualization

Example:
  (topology-layers-string)
  ;; => "Layer 0 (foundation):
  ;;       :synthigy/transit
  ;;
  ;;     Layer 1:
  ;;       :synthigy/database → [:synthigy/transit]
  ;;
  ;;     Layer 2:
  ;;       :synthigy/dataset → [:synthigy/database]
  ;;
  ;;     Layer 3:
  ;;       :synthigy/iam → [:synthigy/dataset]
  ;;       :synthigy/lacinia → [:synthigy/dataset]"
raw docstring

with-storecljmacro

(with-store store & body)

Execute body with a specific LifecycleStore bound to lifecycle-store.

Useful for testing or temporary overrides.

Arguments: store - Implementation of LifecycleStore protocol body - Expressions to execute with the store bound

Example: (with-store (->FileLifecycleStore ".test-lifecycle") (setup! :my/app) (cleanup! :other-app))

Execute body with a specific LifecycleStore bound to *lifecycle-store*.

Useful for testing or temporary overrides.

Arguments:
  store - Implementation of LifecycleStore protocol
  body  - Expressions to execute with the store bound

Example:
  (with-store (->FileLifecycleStore ".test-lifecycle")
    (setup! :my/app)
    (cleanup! :other-app))
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close