本文へ移動
cccskills
無料GitHub で公開

grain-query-handler

Implement query handlers that read from event-sourced read models

インストール方法を見る

含まれるファイル(1)

  • SKILL.md4.3 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Grain Query Handler Pattern

Required Imports

(ns my.service.core.queries
  (:require [ai.obney.grain.event-store-v3.interface :as es]
            [ai.obney.grain.query-processor.interface :refer [defquery]]
            [my.service.interface.read-models :as rm]
            [cognitect.anomalies :as anom]))

Query Template

(defquery :namespace query-name
  "Docstring describing what the query returns."
  [{{:keys [entity-id filter-param]} :query
    :keys [event-store other-dependency]}]

  (let [entity (rm/get-entity event-store entity-id)]
    (if entity
      {:query/result {:entity entity}}
      {::anom/category ::anom/not-found
       ::anom/message "Entity not found"})))

Return Patterns

Success

{:query/result {:entity data
                :related-data more-data}}

Error

{::anom/category ::anom/not-found
 ::anom/message "Human readable message"}

Fat Query Pattern (Screen Queries)

Load all data needed for a UI screen in one query:

(defquery :advisor meetings-screen
  "Fat query for meetings screen - returns all data needed."
  [{{:keys [student-id]} :query
    :keys [event-store]}]

  (let [;; Get primary entity
        student (rm/get-student event-store student-id)
        ;; Get related data
        meetings (rm/get-meetings-for-student event-store student-id)
        ;; Get metadata/options
        meeting-types (rm/get-meeting-types event-store)]

    (if student
      {:query/result {:student student
                      :meetings meetings
                      :meeting-types meeting-types}}
      {::anom/category ::anom/not-found
       ::anom/message "Student not found"})))

List Query with Pagination

(defquery :crm list-contacts
  "List contacts with optional filters and pagination."
  [{{:keys [type-slug status limit offset]} :query
    :keys [event-store]}]

  (let [;; Get all matching contacts
        all-contacts (cond->> (rm/get-contacts-all event-store)
                       type-slug (filter #(= type-slug (:type-slug %)))
                       status (filter #(= status (:status %))))
        total (count all-contacts)
        ;; Apply pagination
        paginated (cond->> all-contacts
                    offset (drop offset)
                    limit (take limit))]

    {:query/result {:contacts (vec paginated)
                    :total total}}))

Batch Loading Pattern

Pre-load related data to avoid N+1 queries:

(defquery :crm list-contacts-with-types
  [{{:keys [type-slug]} :query
    :keys [event-store]}]

  (let [;; Pre-load all contact types (1 query instead of N)
        types-by-id (rm/get-contact-types-by-id event-store)
        ;; Get contacts
        contacts (rm/get-contacts-by-type event-store type-slug)
        ;; Enrich with type data
        enriched (mapv #(assoc % :contact-type (get types-by-id (:type-id %)))
                       contacts)]

    {:query/result {:contacts enriched}}))

Composing Read Models

(defquery :crm get-contact
  "Get contact with attribution in single query."
  [{{:keys [contact-id]} :query
    :keys [event-store]}]

  (let [contact (rm/get-contact event-store contact-id)]
    (if contact
      (let [;; Get related data
            relationships (rm/get-relationships-for-contact event-store contact-id)
            attribution (rm/get-attribution event-store contact-id)]
        {:query/result {:contact (cond-> contact
                                   attribution (assoc :attribution attribution)
                                   (seq relationships) (assoc :relationships relationships))}})
      {::anom/category ::anom/not-found
       ::anom/message "Contact not found"})))

Conventions

  • Queries are read-only (never emit events)
  • Use read models to get current state
  • Return {:query/result {...}} for success
  • Return anomalies for not-found and errors
  • Use fat queries for screen data (reduces round trips)
  • Batch load related data to avoid N+1 queries
  • Include pagination metadata (:total) for list queries

Reference Files

  • components/crm-service/src/ai/obney/workshop/crm_service/core/queries.clj - CRM queries with batch loading

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

allium

無料

Give your AI agents something more useful than a prompt. Velocity through clarity.

日本語の概要は準備中です。原文の説明を表示しています。

ObneyAI/orc212026年10月8日 更新

distill

無料

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.

日本語の概要は準備中です。原文の説明を表示しています。

ObneyAI/orc212026年10月8日 更新

elicit

無料

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.

日本語の概要は準備中です。原文の説明を表示しています。

ObneyAI/orc212026年10月8日 更新

Implement command handlers that validate state and emit events using the Grain framework

日本語の概要は準備中です。原文の説明を表示しています。

ObneyAI/orc212026年10月8日 更新

Create scheduled background jobs that run on cron schedules

日本語の概要は準備中です。原文の説明を表示しています。

ObneyAI/orc212026年10月8日 更新

Build read models by reducing events into queryable state

日本語の概要は準備中です。原文の説明を表示しています。

ObneyAI/orc212026年10月8日 更新

ObneyAI のスキルをすべて見る

このスキルの問題を報告する