본문으로 건너뛰기
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

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

allium

무료

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

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 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/orc222026년 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/orc222026년 10월 8일 업데이트

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

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 8일 업데이트

Create scheduled background jobs that run on cron schedules

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 8일 업데이트

Build read models by reducing events into queryable state

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 8일 업데이트

ObneyAI의 스킬 모두 보기

이 스킬의 문제 신고