allium
무료Give your AI agents something more useful than a prompt. Velocity through clarity.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Create a complete Grain domain service with schemas, read models, commands, queries, and tests
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
설치 방법 보기설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.
This skill walks through creating a domain service component in the ORC library. No UI, no web layer — services expose data through commands, queries, and read models.
Naming conventions:
widgets (kebab-case for ns, used as event/command prefix)widget (singular)components/widget-service/components/widget-service/
deps.edn
src/ai/obney/orc/widget_service/
interface.clj
interface/schemas.clj
core/
read_models.clj
commands.clj
queries.clj
todo_processors.clj
test/ai/obney/orc/widget_service/
test_helpers.clj
commands_test.clj
deps.edn:
{:paths ["src" "resources"]
:deps {}
:aliases {:test {:extra-paths ["test"]
:extra-deps {}}}}
interface/schemas.clj)(ns ai.obney.orc.widget-service.interface.schemas
(:require [ai.obney.grain.schema-util.interface :refer [defschemas]]))
(defschemas events
{:widgets/widget-created [:map [:widget-id :uuid] [:name :string] [:description [:maybe :string]]]
:widgets/widget-updated [:map [:widget-id :uuid] [:name :string] [:description [:maybe :string]]]
:widgets/widget-deleted [:map [:widget-id :uuid]]})
(defschemas commands
{:widgets/create-widget [:map
[:name [:string {:min 1 :error/message "Name is required"}]]
[:description {:optional true} [:maybe :string]]]
:widgets/update-widget [:map
[:widget-id :uuid]
[:name [:string {:min 1 :error/message "Name is required"}]]
[:description {:optional true} [:maybe :string]]]
:widgets/delete-widget [:map
[:widget-id :uuid]]})
(defschemas queries
{:widgets/get-all [:map]
:widgets/get-by-id [:map [:widget-id :uuid]]})
core/read_models.clj)(ns ai.obney.orc.widget-service.core.read-models
(:require [ai.obney.grain.read-model-processor-v2.interface :as rmp :refer [defreadmodel]]))
(def widget-event-types
#{:widgets/widget-created
:widgets/widget-updated
:widgets/widget-deleted})
(defmulti widgets* (fn [_state event] (:event/type event)))
(defmethod widgets* :widgets/widget-created
[state {:keys [widget-id name description]}]
(assoc state widget-id {:widget-id widget-id :name name :description description :active true}))
(defmethod widgets* :widgets/widget-updated
[state {:keys [widget-id name description]}]
(update state widget-id merge {:name name :description description}))
(defmethod widgets* :widgets/widget-deleted
[state {:keys [widget-id]}]
(dissoc state widget-id))
(defmethod widgets* :default [state _] state)
(defreadmodel :widgets widgets
{:events widget-event-types :version 1}
[state event]
(widgets* state event))
;; Query helpers
(defn get-widget [ctx widget-id]
(get (rmp/project ctx :widgets/widgets {:tags #{[:widget widget-id]}})
widget-id))
(defn get-all-widgets [ctx]
(->> (vals (rmp/project ctx :widgets/widgets))
(filter :active)
(sort-by :name)))
core/commands.clj)(ns ai.obney.orc.widget-service.core.commands
(:require [ai.obney.orc.widget-service.core.read-models :as rm]
[ai.obney.grain.event-store-v3.interface :refer [->event]]
[ai.obney.grain.command-processor-v2.interface :refer [defcommand]]
[cognitect.anomalies :as anom]))
(defcommand :widgets create-widget
"Create a new widget."
[{{:keys [name description]} :command :as ctx}]
(let [widget-id (random-uuid)]
{:command-result/events
[(->event {:type :widgets/widget-created
:tags #{[:widget widget-id]}
:body {:widget-id widget-id
:name name
:description description}})]}))
(defcommand :widgets delete-widget
"Delete a widget."
[{{:keys [widget-id]} :command :as ctx}]
(if-let [widget (rm/get-widget ctx widget-id)]
{:command-result/events
[(->event {:type :widgets/widget-deleted
:tags #{[:widget widget-id]}
:body {:widget-id widget-id}})]}
{::anom/category ::anom/not-found
::anom/message "Widget not found."}))
core/queries.clj)Queries return data — no UI rendering in a library.
(ns ai.obney.orc.widget-service.core.queries
(:require [ai.obney.orc.widget-service.core.read-models :as rm]
[ai.obney.grain.query-processor.interface :refer [defquery]]))
(defquery :widgets get-all
"Get all active widgets."
[ctx]
{:query/result {:widgets (rm/get-all-widgets ctx)}})
(defquery :widgets get-by-id
"Get a widget by ID."
[{{:keys [widget-id]} :query :as ctx}]
(let [widget (rm/get-widget ctx (parse-uuid widget-id))]
(if widget
{:query/result {:widget widget}}
{:query/result {:widget nil}})))
interface.clj)(ns ai.obney.orc.widget-service.interface
(:require ;; Side-effect requires — registers commands/queries/processors
[ai.obney.orc.widget-service.core.commands]
[ai.obney.orc.widget-service.core.queries]
[ai.obney.orc.widget-service.core.todo-processors]
;; Re-export read model helpers
[ai.obney.orc.widget-service.core.read-models :as rm]))
(def get-widget rm/get-widget)
(def get-all-widgets rm/get-all-widgets)
core/todo_processors.clj)(ns ai.obney.orc.widget-service.core.todo-processors
(:require [ai.obney.grain.todo-processor-v2.interface :refer [defprocessor]]))
;; Add defprocessor forms here when event-driven side effects are needed
(ns ai.obney.orc.widget-service.commands-test
(:require [clojure.test :refer [deftest testing is]]
[ai.obney.orc.grain-test-utils.interface :as tu]
[ai.obney.orc.widget-service.core.commands]
[ai.obney.orc.widget-service.interface.schemas]
[cognitect.anomalies :as anom]))
(deftest create-widget-test
(testing "creates a widget"
(tu/with-test-context [ctx]
(let [result (tu/process-command! ctx
{:command/name :widgets/create-widget
:name "Test Widget"
:description "A test"})]
(is (tu/event-of-type? result :widgets/widget-created))
(let [event (tu/find-event result :widgets/widget-created)]
(is (= "Test Widget" (:name event))))))))
deps.edndeps.ednAdd to :aliases > :dev > :extra-deps:
orc/widget-service {:local/root "components/widget-service"}
projects/orc/deps.ednAdd to :deps:
orc/widget-service {:local/root "../../components/widget-service"}
components/orc-service/ — Core ORC service (comprehensive example)components/colbert/ — ColBERT service (commands, read models, queries)components/ontology/ — Ontology service (event sourcing patterns)아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.
로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.
Give your AI agents something more useful than a prompt. Velocity through clarity.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Extract an Allium specification from an existing codebase. Use when the user has existing code and wants to distil behaviour into a spec, reverse engineer a specification from implementation, generate a spec from code, turn implementation into a behavioural specification, or document what a codebase does in Allium terms.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Run a structured discovery session to build an Allium specification through conversation. Use when the user wants to create a new spec from scratch, elicit or gather requirements, capture domain behaviour, specify a feature or system, define what a system should do, or is describing functionality and needs help shaping it into a specification.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Implement command handlers that validate state and emit events using the Grain framework
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Create scheduled background jobs that run on cron schedules
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Implement query handlers that read from event-sourced read models
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.