記法クックブック — karasu でモデリングするための idiom 集
English · 日本語(このファイル)
docs/spec/syntax.md は文法そのもの — すべてのキーワードと
規則です。このクックブックはその欠けていた相棒で、「X をどう表現するか」に答える
コンパクトな idiom カタログです。文法からは到達できるが、文法を読んだだけでは
自明でないパターンを対象にします。各エントリはコピーして使える worked snippet です。
意図的に短くしてあるので、プロジェクトをリバースエンジニアリングするとき
syntax.md と一緒に LLM へ渡せます(LLM でリバースする
を参照)。人間のオンボーディング資料も兼ねます。生成されたモデルは常に仕様ではなく
地図として扱い、コードと照合してください。
- LLM と使う:
syntax.mdの後にこのファイルを貼る。文法だけでなく idiom を 示すので、モデルが独自の形を発明せず karasu 慣用の形を選ぶ。 - 人間が読む: 必要なパターンを拾い読みする。各エントリは単独で完結している。
各エントリは同じ形をとる: いつ使うか(When)、規則を 1 行で述べたパターン、
最小の .krs、そしてなぜそう書くか(Why)。
1. キー/バリューストア(Redis, etcd)— leaf-less な database
Section titled “1. キー/バリューストア(Redis, etcd)— leaf-less な database”When — table/collection の構造としてモデル化する意味がないストア: セッション キャッシュ、KV/コンフィグストア、ロックサービスなど。
パターン — leaf を持たない database を宣言し、サービスからノード粒度の
エッジ(Service --> Store)でつなぎ、具体的なエンジンは物理層の store ユニット
で名指しする。@kv アノテーションや新しい kv kind を凍結してはいけない — KV
ストアは単に分解しない database である。
system Web { service ApiGateway { label "API Gateway" }
database SessionStore [cache] { // leaf-less: 中に `table` を書かない label "Session store" }
ApiGateway --> SessionStore "Reads/writes session tokens" // ノード粒度のエッジ}
deploy "production" { store "session-kv" { type "Redis 7" // 具体的なエンジンはここ realizes SessionStore }}Why
resourceの dot-path ではなくエッジでノード粒度参照する。resource <Db>.<Leaf>参照には宣言済みの leaf が要る。KV ストアには名前を付ける leaf が無いので、resource SessionStoreと書くと未割り当てになる(warning が出て、ノードは自身の usecase の中にしか描かれず、本来ストアが居るべき system ビューには出てこない)。 慣用的な接続は直接のService --> SessionStoreエッジで、hato例が leaf-less なD1/R2/Tasksを配線しているのと同じ方式。- エンジンは物理層に。 「Redis 7」は技術の選択。論理層は技術非依存に保ち、
store { type … }ユニットがどのエンジンが論理ストアを realize するかを記録する。 Redis を etcd に載せ替えても論理モデルは揺れない。 - 語彙を増やさない。 アノテーション(
@…)はライフサイクル標識(deprecated, experimental)であって kind ではない —@kvは意図的に却下。leaf-less なdatabaseが既に「興味深い下部構造を持たないストア」を表現している。 [cache]は「正本ではない」と言うタグ。 セッションストアが消えると全員が ログアウトするが、業務データは失われない — それがこのタグの判定そのものである。 付けるのは役割であって技術ではない。[kv]が却下なのは@kvと同じ理由 (ストアの役割タグ)。
2. 正本ではないストア — [index] / [cache] / [analytics]
Section titled “2. 正本ではないストア — [index] / [cache] / [analytics]”When — system of record から派生した検索/ベクトルインデックス (ElasticSearch, OpenSearch, pgvector)で、それ自体が正本ではないもの。
パターン — database に [index] を付ける。具体的なエンジンは物理層に置く。
database SearchIndex [index] { table documents}
// 物理層deploy "production" { store "search" { type "ElasticSearch 8" realizes SearchIndex }}Why — [index] は技術ではなく役割(正本に対する二次インデックス)を表し、
index バッジを付ける。ベクトル DB や ElasticSearch がそれ自体正本なら素の
database のまま([index] なし)。1 つの Postgres が正本かつインデックスを兼ねる
場合も付けない。idiom #1 と同じ「役割はタグ・技術は物理層」の規律で、エンジンごとに
vector-store / search kind を増やすのを避ける。
ADR-1718 を参照。
[index] には同じ軸の兄弟が 2 つあり、いずれも database だけでなく storage にも
付けられる。[cache] は再構築できるストア(セッションストア、CDN のオリジン
キャッシュ。消えて業務データが失われるならそれは正本なのでタグを付けない)、
[analytics] は分析のために取り込んだ DWH / データレイク。タグ無しは正本その
ものを指す。ストアの役割タグを参照。
3. 境界の外側にあるもの — [external] タグ
Section titled “3. 境界の外側にあるもの — [external] タグ”When — システムが依存するが所有はしない、サードパーティ API・マネージド SaaS・ 外部ストア。
パターン — ノード id に [external] を付ける。service と infra kind
(database / queue / storage)に適用できる。
system Shop { service PaymentGateway [external] { label "Payment gateway" }
database AnalyticsDB [external] { // マネージドなサードパーティストア label "Vendor analytics DB" }}Why — [external] はノードを破線・グレー系のボーダーで描き、読者が即座に
システム境界を見て取れる。外部ストアは shared-infra-fan-in 診断(idiom #4)の
対象からも除外される — ベンダー API を複数サービスで共有するのは想定内であって
設計上の匂いではない。
4. 共有インフラ(fan-in)
Section titled “4. 共有インフラ(fan-in)”When — 複数のサービスが同じデータストアを読み書きする。
パターン — ストアは一度だけ宣言し、各サービスの usecase が共有 leaf を
resource <Db>.<Leaf> の dot-path で参照する。resolver がこれらを集約して
service → database エッジを自動導出する。
database ArticleDB { table articles}
service ArticleDelivery { domain Delivery { usecase "Fetch an article" { resource ArticleDB.articles } }}
service Authoring { domain Publishing { usecase "Publish an article" { resource ArticleDB.articles } }}Why — 2 つのサービスが 1 つの ArticleDB に fan-in すると shared-infra-fan-in
の info 診断が出る(決してエラーではない — karasu は結合を可視化するだけで、それを
禁じない)。ここは store に leaf(articles)がモデル化されているので resource
dot-path を使う。leaf-less なストアがノード粒度エッジを使う idiom #1 と対比のこと。
診断リファレンス を参照。
5. ドメイン越え・システム越えの参照
Section titled “5. ドメイン越え・システム越えの参照”When — あるドメインが、別のドメイン・サービス・システムが所有するものに 依存する。
パターン — エッジは参照元のブロック内で宣言する。origin-scope 規則が縛るのは
参照元であって参照先ではないので、ブロックは所有していないものに依存できる。別の
システムへは System.Node dot-notation を使う(参照先システムは ghost として描画)。
system Shop { service BillingService { domain Billing { label "Billing" Billing -> Contract "Created from a contract" // Contract は別サービスにある } }
// システム越え: PaymentGateway は別の `system`(別所で import 済み) OrderService -> PaymentGateway.PaymentService "Request payment"}Why — サービス越えのドメインエッジは system ビュー上で暗黙のサービスレベル
エッジに自動導出される。高レベルの絵を読みやすく保ちつつ、詳細は書いた場所に残る。
モデル化していないエンドポイントは破棄されず unresolved-edge-endpoint として
報告される。
6. モデルを複数ファイルに分割する
Section titled “6. モデルを複数ファイルに分割する”When — 1 つの system が単一ファイルには大きすぎる、あるいはチームごとに別々の スライスを所有する。
パターン — ファセット(サービスごとのファイル + 共有 infra.krs)で分割し、
import で綴じ合わせる。同一 id の system / deploy / organization ブロックは
マージ(reopen)される。ファイル全体は import "x.krs"、単一ノードは
import { Node } from "x.krs" で取り込む。
// index.krs — エントリポイントimport "infra.krs" // 共有の database / queue / storageimport "reader.krs" // 1 ファイル 1 サービスimport "editor.krs"
system Blog { label "Blog Platform Demo"}Why — モデルにおいてファイルはグルーピング単位ではない: 分割は純粋に
オーサリングの都合で、マージ結果は単一ファイルと同一に描画される。共有ストアは
1 つの infra.krs にまとめ、各スライスがそれを import することで、各スライスも
単独で描画できる。
multi-file-system
例を参照。
7. Cloudflare Workers — wrangler.toml から
Section titled “7. Cloudflare Workers — wrangler.toml から”いつ — 物理層が wrangler.toml にある Cloudflare Workers のサーバーレスアプリ。
compose / k8s ファイルが無いため、binding を手でモデル化すると具体技術(“D1 (SQLite)”)が
論理ラベルに漏れやすい。
パターン — karasu translate --from wrangler に決定的に抽出させる。adapter は
論理 system(engine-neutral な infra + Worker の service + edge)と物理 deploy を出力し、
具体的な Cloudflare 技術は論理ラベルではなく store { type ... } に落ちる:
system Hato { service Hato { label "hato" }
database DB { } // D1 storage EXPORTS { } // R2 queue TASKS { } // Queues database SEARCH [index] { } // Vectorize — 派生ベクトルインデックス(イディオム #2) database CACHE [cache] { } // KV — 再構築できるストア(イディオム #2) service AI [external] { } // Workers AI — 外部モデルサービス(イディオム #3) service SessionActor [external] { } // Durable Object — 不透明な stateful actor
Hato --> DB // 所有 infra は --> Hato -> AI // 外部 / 他 Worker は -> Hato -> AuthWorker // service binding = Worker→Worker RPC edge}
deploy "hato" { function "hato" { runtime "cloudflare-workers"; realizes Hato } store DBStore { type "Cloudflare D1"; realizes DB } store SEARCHStore { type "Cloudflare Vectorize"; realizes SEARCH }}なぜ — binding→karasu のマッピングは新構文を作らず既存イディオムを再利用する:
Vectorize → database [index](イディオム #2 の派生インデックス)、Workers AI /
Durable Object → service [external](イディオム #3、この adapter からは不透明)、
service binding → -> の communication edge、KV → database [cache]
(イディオム #2 の兄弟の役割 — Worker が再構築できるストア)。
未知の binding 種別は warning を出して skip する — 決して推測しない。実行は
karasu translate --from wrangler wrangler.toml > index.krs。
8. 横断的関心事(PCI・PII・認証)— 正しい register を選ぶ
Section titled “8. 横断的関心事(PCI・PII・認証)— 正しい register を選ぶ”When — コンプライアンスのスコープ、データ分類、ポリシー(「この service 群は
PCI 対象」「この entity は PII」「この usecase はログイン必須」)を要素に印付けたく
なり、タグ([pci])やアノテーション(@requires_auth)を発明したくなったとき。
Pattern — ラベルは 4 つの register に分かれる。外部で定義された集合への所属は タグでも、アノテーションでもない:
| 言いたいこと | Register | 構文 |
|---|---|---|
| その要素がアーキテクチャ上何であるか | アーキタイプ | 組み込みタグ — [external]、[index] |
| どの開発状態にあるか | lifecycle | 組み込みアノテーション — @deprecated、@new |
| view で peer をどうまとめるか | view 内グルーピング | boundary |
| 外部定義のどの集合に属するか | 所属 | facet(#2065 Part B、導入予定)— それまでは description + link |
.krs — 今日の PCI / 認証の書き方(prose + link)。ルールの内容はモデル化しない:
system Shop { service Checkout { description "PCI DSS 対象 — 評価資料を参照" link "https://wiki.example.com/pci/scope" "PCI スコープ資料" domain Ordering { usecase PlaceOrder { description "認証必須。誰が呼べるかは IAM ポリシーが定める" link "https://wiki.example.com/policies/iam" "IAM ポリシー" } } }}Why — タグ / アノテーションの語彙はツール所有であり、非 builtin 名は v1.x で
tag-not-builtin / annotation-not-builtin の deprecation warning を受け、構文
v2.0 はツール語彙のみを受理する。所属は意味論的にもタグに合わない: database は
PCI スコープに入っていようがいまいが database であり、対象 10 要素中 9 要素にしか
[pci] が付いていない図は偽の監査保証として読まれる。ルールの内容(role・
プラン・条件)は恒久的に prose + link のまま
(ADR-832)で、facet 導入後に第一級に
なるのは適用範囲だけである。本当に必要なのが足りないアーキタイプなら、代わりに
組み込みタグの追加要望を出す —
tags-annotations.ja.md を参照。
docs/spec/syntax.md— 厳密な.krs文法(まずこれを渡す)docs/spec/tags-annotations.md— タグ / アノテーション一覧- LLM でリバースする — 文法と一緒にこのクックブックを渡す
- オンボーディングガイド — 既存システムを図へ読み下す
© 2026 Hiroki Kondo · Licensed underApache-2.0