ワンクリックで
grain-service
Create a complete Grain domain service with schemas, read models, commands, queries, and tests
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Create a complete Grain domain service with schemas, read models, commands, queries, and tests
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | grain-service |
| description | 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)Create scheduled background jobs that run on cron schedules
Build an ORC behavior tree workflow using the DSL
Set up LLM-as-judge evaluation for ORC workflows
Implement event-driven side effects that react to domain events
Implement command handlers that validate state and emit events using the Grain framework
Implement query handlers that read from event-sourced read models