Liking cljdoc? Tell your friends :D

darkleaf.di.core


->memoizeclj

(->memoize & middlewares)

Returns a stateful middleware that caches built components across multiple di/start calls.

Useful in REPL workflows and test fixtures where the same subsystem is started repeatedly — expensive resources like database connections or HTTP servers are built once and reused.

Builds accumulate. When a dependency is overridden or its var is redefined, di/start builds a new component while keeping the old one cached. (di/stop mem) shuts them all down.

;; jetty depends on handler, handler on a datasource
(di/start `jetty mem)                  ;; builds datasource, handler, jetty
(di/start `jetty mem {"PORT" "9090"})  ;; rebuilds jetty; reuses handler and datasource

;; shut down both jetty instances, the handler, and the datasource
(di/stop mem)

REPL workflow and test fixtures:

;; REPL: restart the system without leaking the Jetty port
(def mem (di/->memoize dev-middlewares))
(def sys (di/start `root mem))
(di/stop sys)

;; redefine a component — on next start it and everything that
;; depends on it will be rebuilt; the rest is reused from mem
(defn some-component
  {::di/kind :component}
  [...]
  ...)

(def sys (di/start `root mem))

;; release everything held in mem
(di/stop mem)

;; Tests: def the memoized registry so each deftest can start
;; its own subsystem against it
(def mem (di/->memoize test-middlewares))

(t/deftest sample
  (with-open [sys (di/start `root mem)]
    ...))

;; tear down at the end of the suite
(di/stop mem)

mem must come before any other middleware:

(di/start `root mem {::override :x})   ;; ok
(di/start `root {::override :x} mem)   ;; throws ::wrong-memoized-registry-position

mem is AutoCloseable. Stop it with (di/stop mem) to release all memoized components.

Returns a stateful middleware that caches built components across
multiple `di/start` calls.

Useful in REPL workflows and test fixtures where the same subsystem
is started repeatedly — expensive resources like database connections
or HTTP servers are built once and reused.

Builds accumulate. When a dependency is overridden or its var is
redefined, `di/start` builds a new component while keeping the old
one cached. `(di/stop mem)` shuts them all down.

```clojure
;; jetty depends on handler, handler on a datasource
(di/start `jetty mem)                  ;; builds datasource, handler, jetty
(di/start `jetty mem {"PORT" "9090"})  ;; rebuilds jetty; reuses handler and datasource

;; shut down both jetty instances, the handler, and the datasource
(di/stop mem)
```

REPL workflow and test fixtures:

```clojure
;; REPL: restart the system without leaking the Jetty port
(def mem (di/->memoize dev-middlewares))
(def sys (di/start `root mem))
(di/stop sys)

;; redefine a component — on next start it and everything that
;; depends on it will be rebuilt; the rest is reused from mem
(defn some-component
  {::di/kind :component}
  [...]
  ...)

(def sys (di/start `root mem))

;; release everything held in mem
(di/stop mem)

;; Tests: def the memoized registry so each deftest can start
;; its own subsystem against it
(def mem (di/->memoize test-middlewares))

(t/deftest sample
  (with-open [sys (di/start `root mem)]
    ...))

;; tear down at the end of the suite
(di/stop mem)
```

`mem` must come before any other middleware:

```clojure
(di/start `root mem {::override :x})   ;; ok
(di/start `root {::override :x} mem)   ;; throws ::wrong-memoized-registry-position
```

`mem` is `AutoCloseable`. Stop it with `(di/stop mem)` to release all
memoized components.
sourceraw docstring

add-side-dependencyclj

(add-side-dependency dep-key)

A registry middleware for adding side dependencies. Use it for setup steps and other side effects.

A side dependency is built after the root and its dependencies. Several side dependencies are built in the order they were added. See prepend-side-dependency for side dependencies that are built before the root.

(defn warmup
  {::di/kind :component}
  [{cache `cache}]
  (fill-cache cache))

(di/start ::root (di/add-side-dependency `warmup))
A registry middleware for adding side dependencies.
Use it for setup steps and other side effects.

A side dependency is built after the root and its dependencies.
Several side dependencies are built in the order they were added.
See `prepend-side-dependency` for side dependencies that are
built before the root.

```clojure
(defn warmup
  {::di/kind :component}
  [{cache `cache}]
  (fill-cache cache))

(di/start ::root (di/add-side-dependency `warmup))
```
sourceraw docstring

combine-dependenciesclj

(combine-dependencies)
(combine-dependencies a)
(combine-dependencies a b)

Merges dependency maps.

A dependency map associates a key with a dependency type, either :required or :optional. When the same key appears in both inputs, :required wins over :optional.

A reducing function (0/1/2-arity) suitable for reduce and transduce. Use it when implementing p/dependencies for a custom p/Factory.

Merges dependency maps.

A dependency map associates a key with a dependency type, either
`:required` or `:optional`. When the same key appears in both inputs,
`:required` wins over `:optional`.

A reducing function (0/1/2-arity) suitable for `reduce` and `transduce`.
Use it when implementing `p/dependencies` for a custom `p/Factory`.
sourceraw docstring

deriveclj

(derive key f & args)

Applies f to an object built from key. Returns a factory.

key is depended on as :optional: if it is not defined, f receives nil, so make f nil-safe. args are extra arguments passed to f after the object.

(def port (di/derive "PORT" (fnil parse-long "8080")))

See ref, template.

Applies `f` to an object built from `key`.
Returns a factory.

`key` is depended on as `:optional`: if it is not defined,
`f` receives `nil`, so make `f` nil-safe.
`args` are extra arguments passed to `f` after the object.

```clojure
(def port (di/derive "PORT" (fnil parse-long "8080")))
```

See `ref`, `template`.
sourceraw docstring

env-parsingclj

(env-parsing & {:as cmap})

A registry middleware for parsing environment variables. You can define a dependency of env as a string key like "PORT", and its value will be a string. With this middleware, you can define it as a qualified keyword like :env.long/PORT, and its value will be a number. cmap is a map of prefixes and parsers.

The underlying env dependency is optional: if the variable is not set, the value is nil and the parser is not called.

(defn root [{port :env.long/PORT}]
  ...)

(di/start `root (di/env-parsing :env.long parse-long
                                :env.edn  edn/read-string
                                :env.json json/read-value))
A registry middleware for parsing environment variables.
You can define a dependency of env as a string key like "PORT",
and its value will be a string.
With this middleware, you can define it as a qualified keyword like `:env.long/PORT`,
and its value will be a number.
`cmap` is a map of prefixes and parsers.

The underlying env dependency is optional: if the variable is not set,
the value is `nil` and the parser is not called.

```clojure
(defn root [{port :env.long/PORT}]
  ...)

(di/start `root (di/env-parsing :env.long parse-long
                                :env.edn  edn/read-string
                                :env.json json/read-value))
```
sourceraw docstring

inspectclj

(inspect key & middlewares)

Walks the registry like start, but builds nothing.

Expects the same arguments as start — a key and registry middlewares — and returns the dependency graph as a vector of maps, one per factory the walk visits. The graph is walked exactly as start would walk it — middlewares applied, overrides in place — so the report matches the system you would get.

Each map holds up to three entries:

  • :key — the key naming the factory.
  • :dependencies — a map of each dependency key to :required or :optional. Absent when the factory depends on nothing.
  • :description — a diagnostic map from the factory's own description method. See darkleaf.di.protocols/Factory.

Two markers come from the walk itself. ::di/root true marks each key you asked for. A key that no registry resolves is reported as {::di/kind :undefined}.

(di/inspect `root)
;; => [{:key `root
;;      :dependencies {`foo :required, `bar :optional}
;;      :description  {::di/kind :service, ::di/root true}}
;;     {:key `foo :description {::di/kind :trivial, :object 42}}
;;     {:key `bar :description {::di/kind :undefined}}]

Pass a vector or a map as the first argument to inspect many roots at once.

Reach for it to verify how middlewares reshape a system, debug a wiring mismatch, or feed a dependency-graph visualizer.

Walks the registry like `start`, but builds nothing.

Expects the same arguments as `start` — a `key` and registry
middlewares — and returns the dependency graph as a vector of maps,
one per factory the walk visits. The graph is walked exactly as
`start` would walk it — middlewares applied, overrides in place — so
the report matches the system you would get.

Each map holds up to three entries:

- `:key` — the key naming the factory.
- `:dependencies` — a map of each dependency key to `:required` or
  `:optional`. Absent when the factory depends on nothing.
- `:description` — a diagnostic map from the factory's own
  `description` method. See [[darkleaf.di.protocols/Factory]].

Two markers come from the walk itself. `::di/root true` marks each
key you asked for. A key that no registry resolves is reported as
`{::di/kind :undefined}`.

```clojure
(di/inspect `root)
;; => [{:key `root
;;      :dependencies {`foo :required, `bar :optional}
;;      :description  {::di/kind :service, ::di/root true}}
;;     {:key `foo :description {::di/kind :trivial, :object 42}}
;;     {:key `bar :description {::di/kind :undefined}}]
```

Pass a vector or a map as the first argument to inspect many roots
at once.

Reach for it to verify how middlewares reshape a system, debug a
wiring mismatch, or feed a dependency-graph visualizer.
sourceraw docstring

logclj

(log &
     {:keys [after-build! after-demolish!]
      :or {after-build! (fn no-op [_]) after-demolish! (fn no-op [_])}})

A logging middleware. Calls :after-build! when an object is built during di/start, and :after-demolish! when it is stopped. Both callbacks receive a map {:keys [key object]}.

di/log records only what the middlewares before it define: a key resolved by a middleware added after it is not logged. Put it last to log the whole system.

A logging middleware.
Calls `:after-build!` when an object is built during `di/start`,
and `:after-demolish!` when it is stopped.
Both callbacks receive a map `{:keys [key object]}`.

`di/log` records only what the middlewares before it define:
a key resolved by a middleware added after it is not logged.
Put it last to log the whole system.
sourceraw docstring

ns-publicsclj

(ns-publics)

A registry middleware that interprets a whole namespace as a component. The built component is a map of simple keywords to built objects: each public var name becomes a keyword, e.g. the var handler becomes the key :handler. Unbound vars and vars holding nil are skipped.

The key of a component is a keyword with the namespace :ns-publics and a name containing the name of a target ns. For example :ns-publics/io.github.my.ns.

This enables access to all public components, which is useful for testing.

See the test darkleaf.di.how-to.ns-publics-test.

(di/start :ns-publics/io.github.my.ns (di/ns-publics))
A registry middleware that interprets a whole namespace as a component.
The built component is a map of simple keywords to built objects:
each public var name becomes a keyword, e.g. the var `handler`
becomes the key `:handler`. Unbound vars and vars holding `nil`
are skipped.

The key of a component is a keyword with the namespace `:ns-publics`
and a name containing the name of a target ns.
For example `:ns-publics/io.github.my.ns`.

This enables access to all public components, which is useful for testing.

See the test `darkleaf.di.how-to.ns-publics-test`.

```clojure
(di/start :ns-publics/io.github.my.ns (di/ns-publics))
```
sourceraw docstring

opt-refclj

(opt-ref key)

Returns a factory referring to a possibly undefined key. Produces nil in that case.

See template, ref, derive.

Returns a factory referring to a possibly undefined `key`.
Produces `nil` in that case.

See `template`, `ref`, `derive`.
sourceraw docstring

prepend-side-dependencyclj

(prepend-side-dependency dep-key)

A registry middleware for adding side dependencies that are built before the root and its dependencies. Use it for migrations and other setup steps that must finish before the rest of the system starts.

Several prepended side dependencies are built in the order they were added. All of them are built before the root. See add-side-dependency.

(defn flyway
  {::di/kind :component}
  [{url "DATABASE_URL"}]
  (.. (Flyway/configure)
      ...))

(di/start ::root (di/prepend-side-dependency `flyway))
A registry middleware for adding side dependencies
that are built before the root and its dependencies.
Use it for migrations and other setup steps that must finish
before the rest of the system starts.

Several prepended side dependencies are built in the order
they were added. All of them are built before the root.
See `add-side-dependency`.

```clojure
(defn flyway
  {::di/kind :component}
  [{url "DATABASE_URL"}]
  (.. (Flyway/configure)
      ...))

(di/start ::root (di/prepend-side-dependency `flyway))
```
sourceraw docstring

refclj

(ref key)

Returns a factory referring to a key.

(def port (di/ref "PORT"))
(defn server [{port `port}] ...)

(def routes (di/template [["/posts" (di/ref `handler)]]))

(di/start `root {::my-abstraction (di/ref `my-implementation)})

See template, opt-ref, derive, p/build.

Returns a factory referring to a `key`.

```clojure
(def port (di/ref "PORT"))
(defn server [{port `port}] ...)

(def routes (di/template [["/posts" (di/ref `handler)]]))

(di/start `root {::my-abstraction (di/ref `my-implementation)})
```

See `template`, `opt-ref`, `derive`, `p/build`.
sourceraw docstring

startclj

(start key & middlewares)

Starts a system of dependent objects.

key is a name of the system root. Use symbols for var names, keywords for abstract dependencies, and strings for environment variables.

key is looked up in a registry. By default registry uses Clojure namespaces and system env to resolve symbols and strings, respectively.

You can extend it with registry middlewares. Each middleware can be one of the following forms:

  • a function registry -> key -> Factory
  • a map of key and p/Factory instance
  • nil, as no-op middleware
  • a sequence of the previous forms

Middlewares also allow you to instrument built objects. It's useful for logging, schema validation, AOP, etc. See update-key.

(di/start `root
          {:my-abstraction implementation
           `some-key replacement
           "LOG_LEVEL" "info"}
          [dev-middlewares test-middlewares]
          (when dev-routes?
            (di/update-key `route-data conj (di/ref `dev-route-data))))

Returns a container containing the started root of the system. The container implements AutoCloseable, IDeref, IFn, Indexed and ILookup.

Use with-open in tests to stop the system reliably.

You can pass a vector as the key argument to start many keys:

(with-open [root (di/start [`handler `helper])]
  (let [[handler helper] root]
     ...))

See the tests for use cases. See update-key.

Starts a system of dependent objects.

`key` is a name of the system root.
Use symbols for var names, keywords for abstract dependencies,
and strings for environment variables.

`key` is looked up in a registry.
By default registry uses Clojure namespaces and system env
to resolve symbols and strings, respectively.

You can extend it with registry middlewares.
Each middleware can be one of the following forms:

- a function `registry -> key -> Factory`
- a map of key and `p/Factory` instance
- nil, as no-op middleware
- a sequence of the previous forms

Middlewares also allow you to instrument built objects.
It's useful for logging, schema validation, AOP, etc.
See `update-key`.

```clojure
(di/start `root
          {:my-abstraction implementation
           `some-key replacement
           "LOG_LEVEL" "info"}
          [dev-middlewares test-middlewares]
          (when dev-routes?
            (di/update-key `route-data conj (di/ref `dev-route-data))))
```

Returns a container containing the started root of the system.
The container implements `AutoCloseable`, `IDeref`, `IFn`, `Indexed` and `ILookup`.

Use `with-open` in tests to stop the system reliably.

You can pass a vector as the key argument to start many keys:

```clojure
(with-open [root (di/start [`handler `helper])]
  (let [[handler helper] root]
     ...))
```

See the tests for use cases.
See `update-key`.
sourceraw docstring

stopclj

(stop root)

Stops the root of a system.

If several components throw during stop, the first exception is rethrown and the rest are attached to it as suppressed exceptions.

Stops the root of a system.

If several components throw during stop, the first exception is
rethrown and the rest are attached to it as suppressed exceptions.
sourceraw docstring

templateclj

(template form)

Returns a factory for templating a data-structure. Replaces ref or opt-ref instances with built objects.

(def routes (di/template [["/posts" (di/ref `handler)]]))

See ref and opt-ref.

Returns a factory for templating a data-structure.
Replaces `ref` or `opt-ref` instances with built objects.

```clojure
(def routes (di/template [["/posts" (di/ref `handler)]]))
```

See `ref` and `opt-ref`.
sourceraw docstring

update-keyclj

(update-key target f & args)

A registry middleware for updating built objects.

target is a key to update. f and args are instances of p/Factory. For example, a factory can be a regular object or (di/ref key).

(def routes [])
(def subsystem-routes (di/template [["/posts" (di/ref `handler)]]))

(di/start ::root (di/update-key `routes conj (di/ref `subsystem-routes)))

See start, derive.

A registry middleware for updating built objects.

`target` is a key to update.
`f` and `args` are instances of `p/Factory`.
For example, a factory can be a regular object or `(di/ref key)`.

```clojure
(def routes [])
(def subsystem-routes (di/template [["/posts" (di/ref `handler)]]))

(di/start ::root (di/update-key `routes conj (di/ref `subsystem-routes)))
```

See `start`, `derive`.
sourceraw docstring

with-opencljmacro

(with-open bindings & body)

A clojure.core/with-open variant that supports destructuring in bindings.

bindings => [name init ...] Evaluates body in a try expression with names bound to the values of the inits, and a finally clause that calls (.close name) on each name in reverse order.

A `clojure.core/with-open` variant that supports destructuring in bindings.

`bindings` => `[name init ...]`
Evaluates `body` in a try expression with names bound to the values
of the inits, and a finally clause that calls `(.close name)` on each
name in reverse order.
sourceraw 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