コンテンツにスキップ

コアコンセプト

English · 日本語(このファイル)

karasu はシステムのアーキテクチャを 論理・物理・組織 の三つの面で記述する。 これが karasu の設計の根幹である。 それぞれの面は独立したファイルに分離して書けるが、同じ drill-down の中でまとめて扱える。 なぜこの三つを同じ言語で扱うのかという動機 — Conway の法則と逆コンウェイ戦略 — は後述の「目標と非目標」節で詳しく論じる。

何を・なぜ の観点でシステムを記述する。

system の中には「アクセス経路」(誰がどのクライアント面を経由してサービスに到達するか)と「サービス階層」(各サービスが何を内包するか)の二系統がある:

system
├─ user → client → service (アクセス経路:誰が、どの面を介して、何に到達するか)
└─ service → domain → usecase → resource (サービス階層:各サービスの内部構造)
  • system:アクター、クライアント、自社サービス、外部サービスの関係を示す器
  • user:システムを駆動するアクター([human] / [ai]
  • client:エンドユーザーの委譲で動く、我々が出荷するソフトウェア(mobile / web / desktop / CLI / device / extension / embed)。userservice の間に位置する。タグではなく独立した kind にした理由は ADR-20260428-06 を参照
  • service:独立したビジネス機能の単位
  • domain:サービス内のビジネス上の関心事の境界(DDD の Bounded Context に近い)
  • usecase:ドメイン内の業務・操作
  • resource:usecase が操作する対象(テーブル、外部API、ファイル等)。client 直下にも書ける(localStorage / indexedDB / opfs / file / keychain 等のクライアント側ストレージ)

database / queue / storage は system 直下に共有インフラとして配置することもできる(ADR-20260405-05 参照)。

どのように 動いているかを記述する。論理構造とは別の .krs ファイルに分離する。

deploy → war / oci / job / ...

realizes による論理と物理の対応付け

Section titled “realizes による論理と物理の対応付け”

物理と論理の対応は realizes で明示する。

oci "order-service" {
realizes ECommerce // 物理(具象)→ 論理(抽象)
}

UMLのRealization関係に対応。「このデプロイ単位がこのサービスを実現している」という宣言。

デプロイ単位は 共有 infra ノードdatabase / queue / storage)も realize できる — 通常は専用の store kind を使う — ことで、論理データストアを裏付ける concrete なマネージドストアを記録する(例: store "order-db" { type "Aurora PostgreSQL 15"; realizes OrderDB })。これは deploy のランタイム契約層に収まり、インフラのトポロジ(リージョン・AZ・ノード)は対象外のまま。

誰が所有するか を記述する。 サービスとドメインごとのオーナーシップをチーム単位で明示することで、 アーキテクチャ議論とチーム構成の議論を同じテーブルで扱えるようにする。

organization → team → member

組織図を karasu の語彙に含めている理由は、それがアーキテクチャ設計に直結するからである。 詳しくは後述の三面構造の目標を参照。

owns による組織と論理/物理の対応付け

Section titled “owns による組織と論理/物理の対応付け”

どのチームがどのサービスやドメインを所有するかは、team の中で owns により明示する。

organization "ec-org" {
team "ec-team" {
owns ECommerce
owns Order
owns Catalog
}
}

realizes が物理と論理を結ぶのと対称に、owns は組織と論理/物理を結ぶ。 同じノード id を複数の team が owns することはできず、 オーナーシップが重複すると warning として検出される。 これにより、三つの面は独立に書けながらも、対応関係は常に図の中に現れる。

.krs テキストを生成する経路は複数ある — ユーザーがエディタで手書きする、karasu translate コマンドで既存のコード資産 (Docker Compose、Kubernetes マニフェスト、OpenAPI スキーマ、SQL スキーマ)から抽出する、 Chat パネルで対話的に生成する。 どの経路であっても最終的な成果物は .krs テキストであり、図はそこから生成される。 この「テキスト = single source of truth」の原則は後述の「目標」節で詳しく扱う。

Related ADR topics: core-concepts, parser, project


ドリルダウン型アーキテクチャ把握

Section titled “ドリルダウン型アーキテクチャ把握”

ツール名「karasu(鴉)」の由来でもあるコンセプト。 世界を俯瞰して情報を集め、必要な場所へ降りていく鴉のように、 図をドリルダウンしながらアーキテクチャを把握していく。

system(全体俯瞰)
└─ service(ビジネス機能)
└─ domain(関心事の境界)
└─ usecase(業務)
└─ resource(操作対象)

任意のノードからドリルダウンして詳細図へ遷移できる。 インラインネストで記述し、育ったら外部ファイルに extract する。

この仕組みは単なるナビゲーションの便宜ではなく、 一度に見せる情報量を限定する ための認知設計上の選択である。 全体を 1 枚に押し込む “at a glance” な鳥瞰図ではなく、 限定された範囲を見てから必要に応じて降りていく scoped glance を first class とする。 なぜこの方針を採るのかは後述の「目標と非目標」節で詳しく論じる。 本ドキュメント内で以降 “scoped glance” と呼ぶ時は、この原則を指している。

Related ADR topics: navigation, renderer


ノード間の関係を表すのが エッジ である。 karasu のエッジモデルは 4 つの仕組みが組み合わさって成立しており、 それぞれが scoped glance と drill-down の原則 をエッジ層で具現化している:

  1. explicit と implicit — 書き手は詳細に書き、読み手は俯瞰で受け取る非対称性
  2. 集約 — 俯瞰時に情報量を絞るための畳み込み
  3. ghost — 視野が狭まっても外との境界を失わない仕組み
  4. 自動検査 — 構造の健全性を静的に保証する

それぞれを動機の観点から説明する。

explicit と implicit — 書き手と読み手の非対称性

Section titled “explicit と implicit — 書き手と読み手の非対称性”

ユーザーが直接書くエッジ(sync は ->、async は -->)を explicit edge と呼ぶ。 一方、ユーザーが異なるサービスに属するドメイン間にエッジを書くと、 karasu は上位の service 間のエッジを 自動的に合成 する。 これが implicit edge で、合成されたエッジには [implicit] タグが付与され、 俯瞰図で琥珀色で描画される。

この仕組みの動機は drill-down と対になる非対称性にある: 書き手はドメインモデリングの結果として細部にエッジを書くだけでよく、 読み手は service レベルの俯瞰図にそれが自動反映される。 ドメインモデリングの成果物がそのままサービス境界の対話に接続され、 ドメインを書くことと俯瞰を読むことの間に手作業の翻訳が挟まらない。

sync と async の区別は視覚的装飾ではなく構造の意味論を扱う情報である — 後述の循環検出が非同期を「意図された疎結合」として扱う根拠になっている。

参照: ADR-20260410-01、ADR-20260413-02。詳細構文は docs/spec/syntax.md

同じ service ペアに複数の domain エッジがあると、 俯瞰図には N 本の線が重ねて描かれることになり、図が読めなくなる。 karasu はこれを sync/async 種別ごとに 1 本の implicit エッジへ畳み込み、 集約ラベルをクリックすると内訳を詳細パネルで確認できる。

これは「目標と非目標」節の 一度に見せる情報量を絞る 原則の直接的な具現化である: 俯瞰時は畳み、必要な詳細は drill-down と詳細パネルに委ねる。 集約は sync / async を別々に扱うため、同じペアに両方の依存があれば 2 本描かれる。

参照: ADR-20260410-01、ADR-20260413-02、PR #607。

ghost — drill-down で視野が狭まっても境界を失わない

Section titled “ghost — drill-down で視野が狭まっても境界を失わない”

drill-down で service や domain の内部に降りると、外の世界は視野の外に出る。 しかし完全に消してしまうと「このドメインは何と依存しているのか」という 境界の文脈 が失われる。 karasu はこれを ghost domain / ghost system で解決する: 視点の外にあるが依存関係を持つノードは、半透明のプレースホルダとして描画される。 境界の存在だけは見え、詳細は見えない — これも scoped glance の具現化である。

加えて、SystemId.ServiceId のドット記法で異なるシステムへ明示的にエッジを引くこともでき、 参照先のシステムは ghost system として描画される。 マイクロサービスや複数組織にまたがるアーキテクチャを描く際の標準的な仕組み。

参照: ADR-20260404-09、ADR-20260405-07、ADR-20260411-05。

アノテーションの継承 — drill-down で文脈を保つ

Section titled “アノテーションの継承 — drill-down で文脈を保つ”

エッジ自体ではなくエッジの端点となるノードのスタイルに関わる仕組みとして、 親 service のアノテーションが子ノード(domain / usecase / resource)に継承される。 service に @deprecated が付いていれば、配下のノードも描画時に deprecated として扱われる (子が自分自身のアノテーションを持てばそこで止まる)。

これは drill-down の任意のレベルで「この service は廃止予定である」という文脈を 保ちながら描画できるようにするための仕組みで、エッジの描画スタイルにも影響する。 深い drill-down 先で急にアノテーションが無視されてしまうことを防ぐ。

参照: ADR-20260415-01。

karasu は sync edge のみを対象に循環依存を自動検出 する。 async を検査対象から外しているのは、非同期依存が「意図された疎結合」を表しており、 循環的な通信があっても起動順序や呼び出しチェーンの問題には直結しないためである。 Sync の循環は実際の障害に繋がるため、静的に検出して警告する価値が高い。

検出された循環エッジには [cyclic] タグが付与されて赤色で描画される。 「ゆっくり変化する構造的な文脈」に留まる karasu だからこそ、 こうした静的検査の結果が意味を持つ — 実装が変わるたびに警告が揺れ動く性質の検査ではない。

参照: ADR-20260405-06。

4 つの仕組み(explicit/implicit、集約、ghost、循環検査)は独立した機能ではなく、 scoped glance + drill-down という同じ原則をエッジ層で言い換えたものである。 書き手は詳細に書き、読み手は限定された視野で受け取り、 境界は ghost で残り、静的検査が構造の健全性を守る。

Related ADR topics: edges


karasu が「描く」もの、「規定しない」もの

Section titled “karasu が「描く」もの、「規定しない」もの”

karasu は architecture を 視覚化 する。流派(school)を 規定 することはしない。 扱うのは「何が ある か」であり、「ある流派の観点で何が あるべき か」ではない。 これは「目標と非目標」節の「ゆっくり変化する構造的事実」というフィルタの上に重ねる 第二の軸 に当たる。karasu のスコープ内であっても、ある流派の観点で smell と される構造(複数サービスから共有される database、複数サービスにまたがる domain など)は 存在しうる。karasu の役目は それを明確に描き、チームの判断に委ねる ことであり、 スタイル違反として render を拒否することではない。

実運用としては次のように働く:

  • ユーザーが書いたものは、そのまま図にする。 database UserDB を 3 ファイルから 参照すれば、1 つの DB ノードに 3 本のエッジが刺さる図になる。domain を複数 service の下に書けば、それぞれの下に描かれる。service [external] が system の中を指していてもそのまま描く。構造として 妥当な記述は、スタイル上の理由で 拒否しない。
  • 流派観点の smell には情報的な診断(infowarning ではない)を出す。 resolver が「ある流派でなら smell とされる構造」を検出したとき、info 診断で 事実を述べ、本セクションへのリンクを 1 つ示す。「それは間違いだ」とは言わない。

このカテゴリに属する診断は 「karasu が何か気付いた — 文脈上気になるなら読み、 気にならなければ無視してよい」 と読むものとする:

診断何を観察しているか流派の文脈
domain-dispersal (info)同一 system 内で同じ domain id が 2 つ以上の service に登場DDD は領域分散を凝集性低下のシグナルとみなす
infra-redeclared-across-files (info)同じ database / queue / storage id が複数ファイルで宣言されるマルチファイル分割の衛生 — 観察しているのは宣言の冗長性であって共有ではない(1 service しか触っていなくても発火しうる)
shared-infra-fan-in (info)同一 system 内で同じ database / queue / storage を 2 つ以上の service が参照する([external] / [index] ストアは除外)マイクロサービスの Database-per-Service は共有 store を smell とする — 何ファイルで宣言したかに依らず、実際の共有で判定する
duplicate-owner-assignment (info)同一ノードを複数の teamowns(主オーナーは最初の宣言を採用)単一所有を前提とする組織論は重複所有を smell とみなす。逆コンウェイの移行期には正当な過渡状態

このリストは今後も伸びる。追加する判断基準は以下のとおり:

  • その構造的事実をある外部の流派が smell と呼ぶか?info 診断、事実先行の 文言、本セクションへのリンク。
  • karasu のモデル自身に関する事実か (宣言されていない id への参照、 指す先のない realizes など)→ 従来どおり error / warning。これは スタイル判断ではなく、モデルの内部整合性の話。
  • karasu 自身が立場を取らないスタイル判断か (「Hexagonal ならこうする」 「Clean Architecture ならこれは禁止」など)→ 診断を 出さない。スコープ外。

なぜこの立場を取るのか: karasu の重心(「目標と非目標」節)は構造的事実にある。 特定流派の好みを core 診断に埋め込むと、karasu は「分野の流行とともに変わる流派の position を取り続け、議論し直し続ける」立場に追い込まれる。事実を見せ、文脈への リンクを添えて、プロジェクト・チーム・制約を知っているユーザーに判断を委ねる — これが本立場の核心である。

同じ理由づけは ADR-20260430-01(セキュリティ / 脅威モデリングを core に取り込まない) や ADR-20260511-02(実行時認可を core に取り込まない)にも通底する。いずれも 「外部の規律が推奨する shape を karasu の語彙に固定しない。ただし基礎となる事実は モデル化できるようにしておく」という同じ判断の現れである。

Related TPLs: TPL-20260514-08 — Diagnostic register reflects "fact vs. style"

Related ADR topics: styling

実装ではなく構造 — テストケースとしての client サブ言語

Section titled “実装ではなく構造 — テストケースとしての client サブ言語”

非目標のフィルタは 1 本の線を引く: karasu は 緩やかに変化する構造的文脈 — 何が存在し、どう関係し、誰が所有するか — をモデル化し、実装の詳細や実行時の 状態はその外に置く。ほとんどのプリミティブはこの線から十分に離れている。 client サブ言語は karasu がこの線に 最も近づく場所であり、語彙の中で最も 詳細な一角(form-factor タグ・handlesdeliversresourcecapability)で、 追加のたびに「これは実装に踏み込んでいないか?」という問いを誘う。

踏み込んでいない — 理由は 1 つのテストにある。client の各機能はアクセス パス上の参加者か関係を名指す(誰が・どの surface 越しに・どの種類の状態を保持して・ 何に到達するか)。フレームワーク・スキーマ・ペイロード・実行時の値は、どれも 名指さない。 それが線の構造側である:

  • form-factor タグ[web] / [mobile] / [desktop] / …)は、ユーザーが system に到達する surface の種類 を分類する。フレームワークやスタック(React か SwiftUI か)は 記録しない — それは実装。認識される集合が閉じているのは builtin の扱いのためだけで、タグシステム自体は開いたまま。
  • handles は、client(または service)が 呼び出し側に公開するドメインを 宣言する — 検証付きの構造的な相互参照(one-hop expose 規則で到達可能であること)。 アクセスパス上の到達可能性の話であって、API のリクエスト/レスポンスのペイロード ではない。
  • delivers は、service が 出荷する client を宣言する(BFF / SSR パターン) — 2 つの構造ノード間の所有・出荷関係であり、バンドルを生成する build / deploy パイプラインではない。
  • resource(client 上)は、保持するローカル永続化の 種類 を、小さな 予約集合localStorage / sessionStorage / indexedDB / opfs / file / keychain)から名指す。これが線に最も近い機能で — storage を名指すが — 記録する のは どの種類の状態が surface 上に存在するかだけで、スキーマ・内容・実行時の 値は決して記録しない。予約集合はこの境界を実行可能にしたもの: それ以外(生の 認証情報・cookie・デバイス機能)は client-resource-invalid-kind拒否され、 より強いモデル化が要る関心が構造リストへ無言で漏れ込むのを防ぐ。
  • capability は、client がアーキテクチャ上 何ができるか の開いた集合のラベル — 構造的な capability であって、その実装ではない。

client の機能が意図的に省いた何か — indexedDB の中のデータ、handle した ドメインの契約、deliver の背後のバンドラ — を記録したくなったら、その詳細は ノードの description と外部ドキュメントへの link に置くべきで、新しい語彙には しない。これは非目標と同じ escape hatch である: モデルを構造の高度に保ち、実装の 詳細はアーキテクチャに触れずに変われる場所に置く。

Related TPLs: TPL-20260616-03client の語彙はアクセスパスの構造を名指し、実装の詳細は名指さない。

Related ADR topics: (none — boundary clarification section; the client kind itself is covered by ADR-20260428-06)

同一 system 内で同じ domain id が複数の service 配下に登場するケースを、karasu は info 段の 観察として通知する。前述の「karasu が『描く』もの、『規定しない』もの」節の方針どおり、診断は事実を述べるにとどめ、判断は読み手に委ねる — 「直すべき」とは言わない。

ℹ domain "Order" は複数の service の配下に登場します
- ECommerce
- Legacy
DDD では同じドメインが複数 service にまたがる状態を凝集性のシグナルとみなすことがあります

検出スコープ(設計方針 — 実装は #237 でフォローアップ予定)

Section titled “検出スコープ(設計方針 — 実装は #237 でフォローアップ予定)”

検出は system ブロック単位 で行われる。system は組織的なオーナーシップ境界を表すため、 異なる system にまたがる同名ドメインは意図的な並行モデリングとして扱い、通知しない。

// 通知なし — 異なる system は独立した組織境界
system LegacyPlatform {
service OldBilling { domain Payment { ... } }
}
system NewPlatform {
service PaymentService { domain Payment { ... } }
}
// info 通知あり — 同じ system 内で domain id が重複
system ECPlatform {
service ECommerce { domain Order { ... } }
service Legacy { domain Order { ... } } // ← 警告
}

検出キー(設計方針 — 実装は #237 でフォローアップ予定)

Section titled “検出キー(設計方針 — 実装は #237 でフォローアップ予定)”

ドメインの同一性は id で判定する。label(表示名)は翻訳・略称変更が起こりうるため、 検出キーには使用しない。

Related ADR topics: resolver


karasu は C4 Model に触発されつつも独自の語彙を採用している。

C4 Modelkarasu変更理由
Context Diagramsystem”context” は意味が曖昧
Containerserviceビジネス機能の単位であることを明示
Componentdomainドメイン境界であることを明示
Codeusecase業務・操作の単位を表現
(なし)resourceusecase が操作する対象を明示
(なし)deploy / realizes物理構造を論理構造と分離して表現

C4 互換性の追求自体は目標ではない。詳細は後述の「目標と非目標」節を参照。

Related ADR topics: (none — comparison / onboarding section)


karasu が何を目指し、何を目指さないかを明示する。 このリストの価値は個々のルールそのものよりも 理由 にある。 将来「X を追加すべきか」を判断する際に、原理から再導出することなく、 ここに書かれた理由を再利用できることが狙い。

karasu はアーキテクチャをテキストで記述する

Section titled “karasu はアーキテクチャをテキストで記述する”

karasu のモデルは .krs テキストとして記述され、図はそこから生成される。 すべての入力経路 — 手書き、translate による既存コードからの抽出、 Chat による対話的生成 — は最終的に .krs テキストに収束する。 テキストを迂回する経路は存在しない。

では なぜテキストを選んだのか。 karasu は論理アーキテクチャ・物理アーキテクチャ・組織アーキテクチャを表現するのに 十分な表現力を持った DSL として設計されているが、この表現手段としてテキストを選んだ理由は 次の 4 つの性質に集約される。 これらは「テキストを選んだ理由」であると同時に、「テキストを採用した結果得られる性質」でもある。

1. 過不足のない表現力を持つ single source of truth になれる

karasu DSL はアーキテクチャを語るのに必要な語彙 (system / service / domain / usecase / resource / deploy / realizes / organization / team)を 提供し、それ以上の実装詳細は表現できない設計になっている。 この「ちょうど良さ」があるからこそ、.krs テキストはモデルの唯一の source of truth として機能できる。 あらゆる入力経路が合成可能になり — Chat の出力を人間が手で編集でき、 逆に手書きしたモデルを Chat で洗練できる — 一貫したメンタルモデルが成立する。

2. エディタによる記述支援を受けられる

テキスト形式であることで LSP のエコシステムに乗れる: 補完、バリデーション、jump-to-definition、hover、リネーム等。 グラフィカルエディタでこれらの支援を同等に提供するのは極めてコストが高いが、 テキスト + LSP では標準化された基盤の上に乗るだけで手に入る。

3. AI との相性が良い

汎用 LLM にアーキテクチャ図を描かせると、出力の形式も抽象度も不安定になる。 karasu の DSL を中間言語として挟むと、AI の出力は karasu の表現力の範囲に制約され、 構造と抽象度が安定する。 karasu の DSL は AI のために設計されたのではなく、人間の記述道具として独立した価値を持つため、 AI と人間の双方向の協働言語として機能する。 詳しくは後述の「karasu と AI — 制約付き中間言語としての DSL」節を参照。

4. diff が計算しやすく、変更が見える

テキスト形式・決定論的な出力・局所的な変更という性質は、 git・ブラウザデモの OPFS・将来の履歴機構のいずれにおいても 「何が変わったか」をユーザーに還元できることを意味する。 PR レビューで「このアーキテクチャ変更は何をした」が一目で分かり、 履歴を遡って「いつこの境界が生まれたか」を辿れる。 UX 上の帰結として「2 つの .krs をグラフィカルに diff 表示する」機能が考えられる(#650 参照)。

具体的には次の性質がこれを支える:

  • テキスト形式であること(バイナリでも自動生成 XML でもない)
  • 出力が決定論的であること — 同じ .krs は常に同じ SVG をレンダリングし、順序が安定している
  • 変更が局所的であること — ノードを 1 つ追加しただけで無関係な領域が diff に現れない

一度に見せる範囲を限定し、ドリルダウンで詳細へ降りる(scoped glance)

Section titled “一度に見せる範囲を限定し、ドリルダウンで詳細へ降りる(scoped glance)”

人間が一度に把握できる情報量には限界がある。 アーキテクチャ全体を 1 枚の図に押し込む “at a glance” な鳥瞰図は、 システムが育つにつれて急速に読めなくなる — 線が交錯し、ノードが小さくなり、読者は図の中から知りたい情報を探すことに 認知リソースを使うことになる。これは図の失敗ではなく、 一度に伝えようとする情報量と人間の認知帯域のミスマッチである。

karasu はこの問題に対して 一度に見せる情報量を常に絞り、必要な詳細があればその場所へ drill-down して降りる というアプローチを first class として採用する。 階層(system → service → domain → usecase → resource)はこの設計を具現化したもので、 単なるナビゲーションの便宜ではなく、認知設計上の選択 である。

karasu は C4 Model からドリルダウンの発想にインスピレーションを受けているが、 C4 が 4 つの固定的な diagram type(Context / Container / Component / Code)として定義するのに対し、 karasu はユーザーが任意のノードから下位へ掘り下げ、インラインネストで書き育て、 成長したら外部ファイルへ extract するという、より連続的・段階的なドリルダウンを採用している。 この連続性は、モデルが育つ過程をそのまま表現できるという利点を持つ — 最初は小さなインラインのネストから始め、育ってから extract すればよい。

補足: この目標は「鳥瞰図には価値がない」と主張するものではない。 システム全体にどのような構造が存在するかを 示唆する 補助的な俯瞰ビュー (構造は見せるが詳細は省く “hint view” のようなもの)は、 drill-down を置き換えるのではなく 補完する 方向で将来の検討対象となりうる。 ただし first class なのはあくまで「限定された範囲 + drill-down」であり、 hint view があるとすればそれはその前提の上に乗るものである。

これは「どこまで深く降りるか」だけでなく「解像度」の話でもある。 「一度に見せる情報量を絞る」は 単一のビューの中でも 破られる。 兄弟ノードが横一列に長く広がると、ビュー全体を画面に収めようと zoom-to-fit が働き、 一つひとつのノードが小さく潰れて 人間が一目で把握できる解像度を割ってしまう。 ノードの 自体は「1 階層分」でも、横方向に伸びるレイアウトは、 全部を 1 枚に詰め込んだ鳥瞰図とまったく同じように認知帯域を超える。 したがって scoped glance は、ナビゲーション(何階層降りるか)だけでなく、 レイアウトが一目で把握できる適度な解像度を保つこと までを含む。 多数の兄弟は横長の一行に流すのではなく、適度な縦横比のグリッドに畳むなどして、 ビュー内の視覚的密度を一定に保つべきである。 (これは balanced-grid な兄弟レイアウトの概念的な裏付けである — Issue #1737 参照。)

Related TPLs: TPL-20260510-21 — 一度に見せる範囲を限定し、drill-down を first-class に保つ(単一ビューの解像度・視覚密度もこの原則に含まれる)。

karasu はシステムの構造を「論理・物理・組織」の三つの面で捉える

Section titled “karasu はシステムの構造を「論理・物理・組織」の三つの面で捉える”

アーキテクチャを語るとき、多くのツールは「コンポーネントがどう組まれているか」(論理構造)か、 「どこで動いているか」(物理構造)の片方、あるいは両方に焦点を当てる。 karasu はここに 組織構造 — どのチームがどのサービスを所有し、誰がメンバーであるか — を 第三の次元として加える。

背景には Conway の法則 がある: ソフトウェアの構造と組織の構造は切り離せず、 チームの境界がサービスの境界を作り、逆にサービスの境界がチームの責任範囲を作る。 片方だけを語ると、もう片方に潜む力学を見逃すことになる。

ただし karasu が組織構造を first class として扱う動機は、もう一歩踏み込んだところにある。 マイクロサービスアーキテクチャの文脈で語られる 逆コンウェイ戦略 (inverse Conway maneuver) — 望ましいソフトウェアアーキテクチャを実現するために、意図的にチーム構造を設計しなおすアプローチ — を アーキテクチャ議論と同じテーブルで扱えるようにすることが狙いである。 サービスとドメインごとのオーナーシップを図の中に明示することで、 「このサービスを分割したい。どのチームが新しい境界を所有すべきか」 「このドメインの責任範囲を誰に預けるか」「チームを作り替えるとして、どこから手を付けるか」 といった問いを、論理構造・物理構造と同じ文脈で議論できるようになる。 karasu にとって組織図はドキュメントではなく、設計判断の対象 である。

karasu はこの三つの面を同じ .krs / .krs.style の語彙で、同じ drill-down の中で扱えるようにしている:

  • 論理: system / service / domain / usecase / resource
  • 物理: deploy / realizes
  • 組織: organization / team / member

これらは別々のツールで描くこともできるが、別々に描くと 対応関係(どのチームがどのサービスを、どの環境で運用しているか)が図の外に出てしまい、 アーキテクチャとチーム構成をセットで動かす議論ができなくなる。 karasu の狙いは、三つの面の交点 — Conway の法則と逆コンウェイ戦略の力学が発生する場所 — を 一つの言語で語れるようにすることである。

非目標を読む前に、ほとんどすべての非目標が従っている 共通のフィルタ を意識してほしい。

karasu が何を扱わないかの判断は、個別の機能ごとにゼロから考えるのではなく、 一つの原則に照らして導ける。その原則はこうである:

karasu が扱うのは ゆっくり変化する構造的な文脈 — 何が存在し、どう関係し、 誰が所有するかであり、実装の詳細も運用の現況もその外側にある。

「X もできるようにしては?」という多くの提案は、表面的には魅力的に見えるが、 共通してひとつの副作用を持つ: 実装や運用の詳細をモデル側に引き込む圧力 である。 コード生成は実装の細部をモデルに書かせる、ランタイムメトリクスは個々の pod をモデルに追わせる、 DB スキーマ設計はテーブル定義をモデルに持たせる、シーケンス図は時間軸をモデルに引き込む。 いずれも karasu が立つべき抽象度の外側へモデルを引っ張る力として働く。

以下の非目標はこの同じ原則の個別の現れである。 読者は各項目を独立したルールとしてではなく、 「またあのフィルタが働いている」 という目で読むと、それぞれの判断がなぜ一貫するのかが見える。

完全に自動化されたレイアウト最適化は追求しない

Section titled “完全に自動化されたレイアウト最適化は追求しない”

テキストソースの可読性が最優先である。 ユーザーがスライドや外部ドキュメント向けにピクセル単位で整った図を必要とする場合、 答えは karasu のレイアウトエンジンをその方向に育てることではなく、 レイアウト調整に特化したツールへ出力を逃がすこと である。 この escape hatch として draw.io エクスポートを予定している(#649 参照)。 この非目標と escape hatch は対として機能するため、セットで理解してほしい。

キャンバス上で図を直接描いてモデルを作るモードは提供しない

Section titled “キャンバス上で図を直接描いてモデルを作るモードは提供しない”

キャンバス上でノードをドラッグしてモデルを作成するモードは目標に含まれない。 モデルは常にテキストとして生成・編集され、図はそのテキストから派生する。 この原則は Chat パネルの位置づけも明確にする: Chat はテキストを生成するのであって、図を編集するのではない。 この非目標が無いと、「キャンバスエディタを追加して」という要望を毎回原理から再評価することになる。

ランタイムメトリクスや稼働中のシステム状態は可視化しない

Section titled “ランタイムメトリクスや稼働中のシステム状態は可視化しない”

karasu が表現するのは 意図された アーキテクチャであり、観測された挙動ではない。 稼働中のレイテンシ、エラー率、インスタンス数といった情報を図に重ねることは、 抽象度のミスマッチを生む: karasu の service はビジネス機能の論理単位だが、ランタイムメトリクスは個々のプロセスや pod インスタンスという、 一段細かい粒度を対象とする。両者を混ぜると次のことが起きる:

  • モデルが本番トポロジ(どの pod か、どのリージョンか)を追う必要が生まれ、ダッシュボード寄りに引っ張られる
  • 図が live telemetry に接続されていないと意味を持たなくなる
  • karasu の重心が「アーキテクチャを語る道具」から「運用を見る道具」へ移動する

観測ビュー用の道具は Datadog / Grafana / Backstage など既に存在しており、そちらが担当すべき領域である。

物理インフラのトポロジ(リージョン・AZ・クラスタ・ノード)はモデリングしない

Section titled “物理インフラのトポロジ(リージョン・AZ・クラスタ・ノード)はモデリングしない”

「本番は us-east-1 の 3 AZ にまたがる K8s クラスタで、staging は単一 AZ」といった インフラトポロジそのものの記述は karasu の対象外とする。 一見すると物理構造として deploy の延長に置けそうに見えるが、実際には抽象度がさらに一段下がる: deploy が扱うのは「どのコード成果物がどのランタイム形態(OCI / Lambda / Job 等)で動くか」という ランタイム契約 であり、それが具体的にどのリージョンのどのクラスタの何番目のノードに載るかは クラウドプロバイダ・IaC・オーケストレータの責務である。

これをモデルに引き込むと次の副作用が生じる:

  • モデルが Terraform / Kubernetes マニフェストの重複物になり、真実の源と二重管理になる
  • 環境ごと(prod / staging / dev)にモデルを分岐させる圧力が生まれ、構造の記述が運用構成の記述に飲み込まれる
  • karasu の重心が「アーキテクチャを語る道具」から「インフラ構成を語る道具」へ移動する

インフラトポロジの可視化は Terraform graph、Backstage、クラウドコンソール、専用の構成図ツールが 既に担当している領域であり、karasu は deploy のランタイム契約層で止める。 この非目標は Issue #28 を通じて検討した結果、採用せずスコープアウトと判断した。

振る舞い・シーケンス・時系列のモデリングは行わない

Section titled “振る舞い・シーケンス・時系列のモデリングは行わない”

karasu が表現するのは 構造 である — 何が存在し、どう関係し、誰が所有しているか。 「A が B を呼び、B が C を呼び、応答が戻る」といった時間軸上のフローは対象外とする。 シーケンス図は Mermaid や PlantUML が別の関心事として適切に扱える領域であり、 karasu の図とシーケンス図は異なる問いに答える道具である。 1 つのファイル内で両方を表現しようとすると、どちらの表現力も薄まる。 シーケンス図をドリルダウン対象として追加する案は Issue #23 で検討したが、 同じ理由でスコープアウトとした。

データベーススキーマのモデリングはしない

Section titled “データベーススキーマのモデリングはしない”

テーブル、カラム、インデックス、外部キー、ER レベルの関係は対象外である。 ここで translate --from db との 非対称性 に注意してほしい: 既存のスキーマを取り込んで domain として 抽象化する ことは目標に含まれる (情報は抽象度が上がる方向 — 詳細が落ちる方向に流れる)が、 karasu モデルの中でスキーマを 設計する ことは反対方向の情報の流れを要求する — 実装詳細をモデルに引き込む圧力を生む。 ER モデリング専用のツールが存在するため、そちらを使うべきである。

karasu は C4 Model に触発されているが、独自の語彙(system / service / domain / usecase / resource)と 独自の drill-down セマンティクスを定義している。 厳密な C4 互換は最初から目標ではない — 視覚的な類似から 誤って互換を期待されないよう、非目標として明記する。 互換を追求すると、karasu の差別化要素である論理/物理分離と drill-down モデルに対して 妥協を強いることになる。

モデルからアプリケーションコードは生成しない

Section titled “モデルからアプリケーションコードは生成しない”

これは正しく押さえるべき最重要の非目標である。 なぜなら、理由付けが translate CLI(逆方向に働く)と 非対称 だからである。

  • translate(code → model) は既存システムを 抽象化して俯瞰する ための入力補助である。 情報は抽象度が上がる方向に流れる: 実装詳細が落ちて、アーキテクチャが読めるようになる。
  • コード生成(model → code) は逆に情報が詳細化する方向に流れる: モデルがコードを駆動できるだけの実装詳細を 持つ必要が生じ、結果として ユーザーに過度に詳細なモデルを書かせることになる。 モデルはコードの重複物と化し、karasu は「アーキテクチャを語る道具」から 「実装を駆動する道具」へと軸を外す。

karasu は明示的に アーキテクチャを語る道具 であり、実装を駆動する道具ではない。 この枠組みは隣接する誘惑(「domain から TypeScript の型定義だけ生成して」 「usecase から OpenAPI スケルトンだけ生成して」)にも一貫した答えを与える — どれもモデルに実装詳細を押し込む圧力を生むため、すべて対象外となる。


これらの目標と非目標は固定されたものではなく、実利用から学んで更新されることを想定している。 ただし更新する際には、個別のルールではなく 理由の層 を議論すべきである — 新しいルールを追加する時、既存の理由と矛盾しないか、共通テーマ(抽象度を保つ、テキストを唯一の情報源とする)に 整合するかを確認することで、karasu の一貫性が保たれる。

Related ADR topics: (none — onboarding / rationale section)


karasu と AI — 制約付き中間言語としての DSL

Section titled “karasu と AI — 制約付き中間言語としての DSL”

目標セクションで触れた「AI との相性が良い」という性質は、karasu の立ち位置を 一段引き上げる洞察につながる。この節ではそれを展開する。

問題: 汎用 LLM にアーキテクチャを描かせると出力が安定しない

Section titled “問題: 汎用 LLM にアーキテクチャを描かせると出力が安定しない”

汎用 LLM に「このシステムのアーキテクチャ図を描いて」と頼むと、出力は二つの軸で不安定になる。

  • 形式の不安定さ: ある時は Mermaid、次は draw.io の XML、さらに次は箇条書きの自然言語
  • 抽象度の不安定さ: ある時はクラス図、次はシーケンス図、さらに次はデプロイ図

形式が違えば再利用が効かず、抽象度が違えば同じシステムを語っているのか判断できない。 結果として、AI の出力はレビューも継続的な編集もできない一回性の成果物になりがちで、 人間とのコラボレーションの基盤として機能しない。

解決: DSL を中間言語として挟む

Section titled “解決: DSL を中間言語として挟む”

karasu の DSL を AI との間に挟むと、出力は karasu の表現力の範囲に制約される。 具体的には:

  • 形式が固定される — 出力は常に .krs テキストである
  • 抽象度が固定される — 使える語彙は system / service / domain / usecase / resource / deploy / realizes / organization / team に限定される
  • 評価可能になる — 出力は karasu compile でパースでき、論理的な妥当性を機械的に検査できる

AI の自由度が奪われるのではなく、適切な粒度に揃えられる。 ドメインモデリングに不要な詳細は表現できず、逆にアーキテクチャを語るのに必要な要素は すべて語彙として用意されている。DSL が「ちょうどいい制約」として機能する。

この枠組みは、AI 領域で既に確立された手法と同じパターンに属する。 近年の LLM API は「自由形式のテキストを返す」モードに加えて、 事前に決めたスキーマに合わせて出力させる モードを提供するようになっている。 以下はその代表例で、どれも「生成する内容」ではなく「生成する形」を先に固定するアプローチである。

  • OpenAI の structured outputs: 事前に JSON schema を渡しておき、LLM の出力が必ずその schema に従うことを保証する機能
  • Claude の tool use: 呼び出し可能な関数のシグネチャと引数型を LLM に渡し、LLM はその型に従った引数を生成する機能
  • karasu の DSL: アーキテクチャ記述専用の語彙(system / service / domain / …)で LLM の出力を制約する

いずれも「汎用 LLM に固定のスキーマを与えて出力を予測可能にする」という共通構造を持つ。 karasu の場合、そのスキーマが アーキテクチャを語るために人間が設計した言語そのもの である、 という点が本質的に異なる。

核心: karasu の DSL は AI のために設計されていない

Section titled “核心: karasu の DSL は AI のために設計されていない”

ここが karasu のポジショニングの中心である。

karasu の DSL は、AI アシスト機能を追加するために後から設計された DSL ではない。 人間がテキストエディタで読み書きし、git で diff を取り、コードレビューで議論するための 独立した人間向けの道具 として設計された。目標セクションで述べた 4 つの性質 (表現力・エディタ支援・AI との相性・diff に優しい)のうち、AI との相性は副次効果の一つに過ぎない。

ところが、この 独立性こそが双方向性を生む

  • AI が生成した .krs を、人間は普通のテキストとして読み、手で編集できる
  • 人間が手書きした .krs を、AI に渡して改善や拡張を依頼できる
  • 生成物も手書きも同じファイルに共存し、どちらが書いたかの区別が残らない
  • 人間と AI は同じモデルに対して交互に貢献することができる

これは「AI 用の入力インターフェース」と「人間用の編集画面」を別々に持つアプローチとは 根本的に異なる。karasu では唯一の source of truth である .krs テキストを、 人間と AI が同じ言語で共同編集する

Chat パネルは便利機能ではなく自然な帰結

Section titled “Chat パネルは便利機能ではなく自然な帰結”

karasu の Chat パネルは、アーキテクチャモデリングツールに後付けで AI 機能を生やしたものではなく、 上記の双方向性から導かれる自然な帰結 として存在している。 DSL が人間にも AI にも読める言語であれば、対話的に .krs を育てるインターフェースは 自ずと成立する。

この位置づけは、karasu の Chat 機能に対する未検証の仮説を含んでいる: karasu の syntax を知らない開発者でも、Chat と対話するだけでアーキテクチャを描ける のではないか、という仮説である。この仮説は Issue #638 で外部ユーザーによる検証を計画しており、 結果によっては karasu の入口設計が大きく変わる可能性がある。

この枠組みを踏まえると、karasu を外部に説明する際の語り口が一段引き上げられる。

  • 以前: 「論理・物理・組織の三面構造、drill-down、マルチファイル合成を持つテキストベース・アーキテクチャモデリングツール」
  • 以後: 「人間と AI が同じ言語でアーキテクチャを語り合うための DSL — 言語そのものがアーキテクチャ記述に必要十分な抽象度で設計されているから成り立つ」

後者の表現は AI を付加機能として紹介するのではなく、 DSL の設計そのものから AI 協働が導かれる ことを前面に出している。 AI の話は karasu の機能リストの 1 項目ではなく、核となる設計判断の帰結である。

Related ADR topics: chat-ai

© 2026 Hiroki Kondo · Licensed under Apache-2.0