CPPB pattern macros: Collect → Promote → Pipeline → Boundary.
Codifies an L3 stratified architecture for data-enrichment workflows. Reuses hive-dsl Result monad for railway-oriented threading.
Roles (carried as :cppb/role var metadata):
:collector — gather raw data from a source. Pure-or-leaf. Returns Result. :promoter — pure data → enriched data. Returns Result. :pipeline — pure orchestration: run collectors, thread through promoters. :boundary — effectful edge: dispatch on Result, fire side effect.
Invariants enforced by convention (not compiler):
CPPB pattern macros: Collect → Promote → Pipeline → Boundary. Codifies an L3 stratified architecture for data-enrichment workflows. Reuses hive-dsl Result monad for railway-oriented threading. Roles (carried as :cppb/role var metadata): :collector — gather raw data from a source. Pure-or-leaf. Returns Result. :promoter — pure data → enriched data. Returns Result. :pipeline — pure orchestration: run collectors, thread through promoters. :boundary — effectful edge: dispatch on Result, fire side effect. Invariants enforced by convention (not compiler): 1. Collectors independent — none depends on another's output 2. Promoters composable — order may vary; data → enriched data 3. Pipeline = pure orchestration, no effects 4. Boundary thin — one fn, Result → response or side effect 5. Data flows DOWN; effects only at boundary
(cppb-role v)Return the :cppb/role metadata of a var or symbol.
Return the :cppb/role metadata of a var or symbol.
(defboundary name args & {:keys [doc on-success on-failure]})Define a CPPB Boundary — Result → effect.
Anaphoric bindings inside the branches:
value — unwrapped :ok value (in :on-success branch)
error — full error map (in :on-failure branch)
(defboundary print-report! [result] :on-success (println "OK:" value) :on-failure (println "ERR:" (:error error)))
Boundary is the ONLY place I/O is permitted in CPPB. Keep it thin: dispatch on Result, fire one effect or return one response.
Define a CPPB Boundary — Result → effect. Anaphoric bindings inside the branches: `value` — unwrapped :ok value (in :on-success branch) `error` — full error map (in :on-failure branch) (defboundary print-report! [result] :on-success (println "OK:" value) :on-failure (println "ERR:" (:error error))) Boundary is the ONLY place I/O is permitted in CPPB. Keep it thin: dispatch on Result, fire one effect or return one response.
(defcollector name docstring? args & body)Define a CPPB Collector — gathers raw data from a source.
Body should return a hive-dsl Result: (r/ok value) or (r/err category data). Pure-or-effectful-leaf; if effectful, wrap I/O in (r/try-effect ...).
(defcollector fetch-user [seed] (r/ok {:id (:user-id seed) :name "alice"}))
(defcollector slurp-config [seed] (r/try-effect (slurp (:path seed))))
Define a CPPB Collector — gathers raw data from a source.
Body should return a hive-dsl Result: (r/ok value) or (r/err category data).
Pure-or-effectful-leaf; if effectful, wrap I/O in (r/try-effect ...).
(defcollector fetch-user [seed]
(r/ok {:id (:user-id seed) :name "alice"}))
(defcollector slurp-config [seed]
(r/try-effect (slurp (:path seed))))(defpipeline name args & {:keys [doc collectors promoters]})Define a CPPB Pipeline — pure orchestration of collectors and promoters.
(defpipeline build-report [seed] :collectors [fetch-user fetch-numbers] :promoters [inject-totals format-greeting])
Expansion semantics:
Pure orchestration only — no effects. Effects live in defboundary.
Define a CPPB Pipeline — pure orchestration of collectors and promoters. (defpipeline build-report [seed] :collectors [fetch-user fetch-numbers] :promoters [inject-totals format-greeting]) Expansion semantics: - Each collector is invoked with the pipeline argument (seed). Each must return a Result. Results are bound by collector name (keyword form) into a single accumulator map. First err short-circuits. - The accumulator is wrapped in (r/ok ...) and threaded through promoters via r/bind. Each promoter receives the unwrapped accumulator and returns a Result. First err short-circuits. Pure orchestration only — no effects. Effects live in defboundary.
(defpromoter name docstring? args & body)Define a CPPB Promoter — pure transformation: data → enriched data.
Receives the unwrapped pipeline accumulator (the merged collector map, then whatever previous promoters returned). Must return a hive-dsl Result.
Pure: no I/O, no global mutation. Composability invariant — promoters should commute when their key sets don't overlap.
(defpromoter inject-totals [data] (r/ok (assoc data :total (reduce + (:numbers data)))))
(defpromoter validate-budget [data] (if (pos? (:budget data)) (r/ok data) (r/err :budget/non-positive {:budget (:budget data)})))
Define a CPPB Promoter — pure transformation: data → enriched data.
Receives the unwrapped pipeline accumulator (the merged collector map, then
whatever previous promoters returned). Must return a hive-dsl Result.
Pure: no I/O, no global mutation. Composability invariant — promoters
should commute when their key sets don't overlap.
(defpromoter inject-totals [data]
(r/ok (assoc data :total (reduce + (:numbers data)))))
(defpromoter validate-budget [data]
(if (pos? (:budget data))
(r/ok data)
(r/err :budget/non-positive {:budget (:budget data)})))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 |