(->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.(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))
```(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`.
(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`.
(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))
```(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.(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.(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)) ```
(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`.
(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))
```(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`.(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:
registry -> key -> Factoryp/Factory instanceMiddlewares 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`.(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.
(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`.
(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`.
(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.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |