Claude CodeでStructurizr DSLを書かせ、C4のコンテキスト図とコンテナ図を作る
docker-composeや依存関係からC4のコンテナを拾ってStructurizr DSLを書かせ、validateで検証してからMermaidに書き出す手順です。hookと許可設定の例つき。
Structurizr DSLは、C4モデルのソフトウェア構成をテキストで書く言語です。1つのモデルを書けば、システムコンテキスト図もコンテナ図も、そこから切り出せます。Claude Codeにリポジトリを読ませてこのDSLを書かせ、validate コマンドで構文とモデルの整合を確かめてから図に書き出す。この流れなら、図が実装とずれたときも差分をgitで追えます。
この記事の手順は、次の3点が柱です。
docker-compose.ymlのサービスを、C4のコンテナ候補として拾わせる- 書かせたDSLは
validateに通し、エラーをClaude Codeに返して直させる - 図の書き出しは
exportでMermaidにし、READMEに置く
1つのDSLから2枚の図が出る仕組み
DSLは model と views の2ブロックで構成します。model に人・システム・コンテナと関係を書き、views でどの要素をどの図に載せるかを指定する形です。1つのモデルから複数の図を作る、というのがStructurizrの基本の考え方です。
図の範囲は、views の include * が決めます。ワイルドカードの意味は図の種類で変わります。
| 図 | include * が含めるもの |
|---|---|
| システムコンテキスト図 | include * が含めるもの対象システム、およびそれに直接つながる人とシステム |
| コンテナ図 | include * が含めるもの対象システム内のすべてのコンテナ、およびそれらに直接つながる人とシステム |
この仕様があるので、書くべきなのは「関係」だけです。人から web コンテナへの関係を1本書けば、コンテキスト図では人と対象システムの関係として自動で現れます。この暗黙の関係はDSLの既定の挙動で、無効にしたいときは !impliedRelationships false を使います。
Dockerのコンテナとは別物だと先に伝える
Claude Codeに docker-compose.yml を読ませると、サービスをそのままコンテナにしがちです。C4の側は「Not Docker!」と明記しています。C4のコンテナは、動いていなければシステムが成り立たないアプリケーションかデータストアを指す語です。
具体的にはWebアプリ、モバイルアプリ、バッチ、データベース、BLOBストアなどです。したがって、リバースプロキシやマイグレーション用の一時コンテナは、図に出すかを人が判断します。プロンプトにこの線引きを書いておくと、手戻りが減ります。
検証と書き出しに使うコマンド
Structurizrのコマンドは validate(検証)、inspect(点検)、export(書き出し)、list(要素の一覧)などが揃っています。実行にはDockerかJava 21以上が必要です。
古い structurizr/cli イメージは、Docker Hubの説明が「今後の更新はない」と案内しています。新しい統合版の structurizr/structurizr に乗り換える前提で、次の形で呼びます。
# 構文とモデルの検証
docker run --rm -v "$PWD":/usr/local/structurizr \
structurizr/structurizr validate -workspace workspace.dsl
# 説明の欠落などの点検(error と warning だけ表示)
docker run --rm -v "$PWD":/usr/local/structurizr \
structurizr/structurizr inspect -workspace workspace.dsl \
-severity error,warning
# 全ビューをMermaidで書き出し
docker run --rm -v "$PWD":/usr/local/structurizr \
structurizr/structurizr export -workspace workspace.dsl \
-format mermaid -output diagrams-workspace に渡すパスは、コンテナから見たパスです。上の例ではカレントディレクトリをマウントしているので、リポジトリ直下からの相対パスで足ります。自動化に進む前に、手元で一度流して出力を確かめてください。
validate が失敗時に非ゼロの終了コードを返すかどうかは、コマンドのページに記載がありません。終了コードで合否を判定する前に、わざと壊したDSLを渡して挙動を確かめておくと安全です。対照的に inspect は、返り値が表示した違反の件数だと書かれています。
リポジトリからコンテナを拾わせるプロンプト
読ませる対象は、実体のあるファイルに絞ります。コードを全部読ませるより、構成を宣言しているファイルから入るほうが根拠を示しやすいからです。
docker-compose.ymlのサービス名、image、ports、depends_onpackage.jsonやpyproject.tomlの依存(DBクライアントや外部SDK)- 環境変数の名前(
DATABASE_URL、PAYMENT_API_URLなど)から読み取れる接続先
次のように頼むと、対応表を先に出させられます。
docker-compose.yml と各サービスの依存定義を読み、C4のコンテナ候補を表にして。
列は「サービス名 / C4での種別(アプリ or データストア) / 技術 / 根拠のファイルと行」。
リバースプロキシや一時コンテナは「除外候補」として別の表にして。
表を私が直してから、workspace.dsl を書いて。表を挟むのは、DSLを書かせる前に人が線引きを直せるからです。Structurizrの公式ページも、プロンプトだけで作らせたDSLには多少の手直しが要ると述べています。直しやすい中間成果物を挟むほうが、DSLを直接直すより速く済みます。
関係は「根拠のあるものだけ」書かせる
depends_on は起動順の指定で、通信の向きとは限りません。関係の線はコンテキスト図の読み味を左右するので、次の規約を CLAUDE.md に置いておきます。
## アーキテクチャ図(docs/architecture/workspace.dsl)
- 関係(->)は、コード・設定・環境変数のどれかで根拠を示せるものだけ書く
- 根拠はDSLのコメントに「# compose.yml:12 api の environment」の形で残す
- depends_on だけを根拠に関係を書かない。通信先の設定を探す
- 編集したら validate を通してから報告する。通らないまま「完了」としない
- ビューのキーは必ず明示する(自動生成のキーは変わりうる)例: 注文サービスをDSLにする
架空の注文サービスを例にします。composeには web、api、db、cache があり、api が外部の決済サービスを呼ぶ構成です。次のDSLは、公式の言語リファレンスの構文に沿った書き方の例です。Claude Codeの実際の出力ではありません。
workspace "Order Service" "注文サービスの構成" {
model {
customer = person "顧客" "商品を注文する"
orders = softwareSystem "注文サービス" "注文の受付と履歴の管理" {
web = container "web" "注文画面を返す" "Next.js"
api = container "api" "注文を処理する" "Node.js"
db = container "db" "注文データを保存する" "PostgreSQL" "Database"
cache = container "cache" "セッションを保持する" "Redis" "Database"
}
payment = softwareSystem "決済サービス" "カード決済を行う" "External"
# compose.yml:3 web の ports
customer -> web "注文する" "HTTPS"
# compose.yml:12 web の environment(API_URL)
web -> api "呼び出す" "HTTP"
# compose.yml:20 api の environment(DATABASE_URL)
api -> db "読み書きする" "SQL"
# compose.yml:21 api の environment(REDIS_URL)
api -> cache "読み書きする" "Redis protocol"
# src/payment/client.ts の fetch 先
api -> payment "決済を依頼する" "HTTPS"
}
views {
systemContext orders "context" {
include *
autoLayout lr
}
container orders "containers" {
include *
autoLayout lr
}
styles {
element "Database" {
shape Cylinder
}
element "External" {
background #999999
}
}
}
}関係はすべてコンテナどうし、またはコンテナと人・外部システムの間に書いています。コンテキスト図に出る「顧客 → 注文サービス」「注文サービス → 決済サービス」は、暗黙の関係として生成されます。書いていない関係が図に出るのは仕様どおりなので、驚かずに確認する場所です。
shape に使える値には Cylinder や WebBrowser などがあり、データベースの円筒表現はタグ Database に結びつけておくと全ビューで揃います。
書かせて、検証して、直させる
手順は4段階で回します。
DSLを仕上げるまでの往復
- 1
対応表を作らせる
composeと依存定義から、コンテナ候補と根拠の表を出させて、人が線引きを直します。
- 2
DSLを書かせる
直した表をもとに
workspace.dslを書かせます。関係のコメントに根拠を残させます。 - 3
validateを流す
エラーがあれば、メッセージをそのままClaude Codeに返して直させます。
- 4
書き出して差分を見る
exportでMermaidを出し、前回の図との差分をレビューします。
3段目は手で回してもよいのですが、.dsl を編集した直後に自動で検証させると、Claude Codeが自分でエラーを読んで直してくれます。PostToolUse hookは、編集系のツールが成功した後に走り、終了コード2で返したstderrをClaudeに見せられます。
.claude/settings.json に次のように置きます。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/scripts/c4-validate-hook.sh"
}
]
}
]
}
}scripts/c4-validate-hook.sh は、編集されたファイルが .dsl のときだけ検証します。
#!/usr/bin/env bash
f=$(jq -r '.tool_input.file_path // empty')
case "$f" in *.dsl) ;; *) exit 0 ;; esac
cd "$CLAUDE_PROJECT_DIR" || exit 0
rel=${f#"$PWD"/}
if ! out=$(docker run --rm -v "$PWD":/usr/local/structurizr \
structurizr/structurizr validate -workspace "$rel" 2>&1); then
echo "$out" >&2
exit 2
fi終了コード2にしているのは、PostToolUseで終了コード0のstderrがClaudeに見えないためです。validate のコマンドページには終了コードの記載がないので、壊したDSLで一度試してからこのhookを入れてください。出力にエラーが出ても終了コードが0なら、スクリプトの if 側を出力の文字列で判定する形に変える必要があります。
Dockerの許可は広げすぎない
hookはClaudeの許可とは別に動きますが、手動で検証と書き出しを任せるときは Bash の許可ルールが要ります。docker run * を許可すると、任意のイメージを任意のマウントで動かせてしまいます。許可は、固定した引数のラッパーに絞るほうが狭く済みます。
{
"permissions": {
"allow": [
"Bash(./scripts/c4-check.sh)",
"Bash(./scripts/c4-export.sh)"
]
}
}ルールはコマンドの一致で判定され、npm run build を許可しても npm run build --watch は一致しません。引数を固定したラッパーなら、許可の範囲がスクリプトの中身に限られます。スクリプトの書き換えは編集の許可で別に止められるので、scripts/ への書き込みはレビュー対象にしておくとよいでしょう。
つまずきやすい点
落とし穴
ビューのキーを省くと、図の配置が保てない
systemContext や container のキーを省くと自動生成になります。公式は、自動生成のキーは時間が経つと変わりうるので手動のレイアウト情報を失いやすい、と書いています。Claude Codeには、キーを必ず付けさせます。
もう1つは、検証が通ることと実装に合っていることの違いです。validate が確かめるのはDSLがワークスペースとして成立するかで、関係の向きや、コンテナの取りこぼしが実装と合っているかは判断できません。根拠コメントを残させるのは、その部分を人がレビューできるようにするためです。
inspect も補助になります。要素の説明が空のままだと違反として拾えるので、Claude Codeに説明を埋めさせる指示につなげられます。重大度は、ワークスペースのプロパティで info や ignore に変えられます。
図をリポジトリに置く
export -format mermaid は、システムランドスケープ、コンテキスト、コンテナ、コンポーネント、ダイナミック、デプロイメントの各ビューに対応しています。Mermaidは、GitHubのREADMEのように自動で描画してくれる場所が多くあります。Structurizrは、DSLとJSONをgitで管理してMermaidかPlantUMLに書き出す進め方を、標準の構成の1つに挙げています。
得られるのは静的な図で、ズームやツールチップはありません。レイアウトも自動のみです。見た目を作り込みたいなら、local コマンドでブラウザの図ビューアを使う道もあります。
PlantUMLで図を管理している場合は、Claude CodeでPlantUMLの図を書かせ、構文検証してから書き出すの検証手順がそのまま参考になります。ER図はClaude CodeでER図をMermaidで起こすで扱っています。
MCPサーバーで検証させる選択肢
Structurizrは、DSLの検証・解析・点検とMermaid・PlantUMLへの書き出しを行うMCPサーバーを公開しています。公開インスタンス(mcp.structurizr.com)もあります。接続は次のコマンドです。
claude mcp add --transport http structurizr https://mcp.structurizr.com/mcpこの場合、DSLの全文が外部のサーバーに送られます。社内システムの構成図は、構成そのものが機密になりえます。手元で動かしたいときは、Docker Hubに structurizr/mcp イメージがあり、-dsl -mermaid のようなパラメータで有効にするツールを選べます。
既存のアーキテクチャ文書との役割分担
オンボーディング用の文章は、図とは別の読み物です。根拠つきの概要文書の作り方は、Claude Codeで新メンバー向けアーキテクチャ概要を書くが詳しいです。DSLの図は、その文書に貼る「地図」の役を持たせると、文章と図が別々に古くなる事態を減らせます。
composeの起動順を直すと、depends_on の条件が変わります。起動順の直し方はClaude Codeでdocker composeの起動競合をhealthcheckで直すを参照してください。図の関係は depends_on を根拠にしないので、この変更で図を直す必要は出ません。
まとめ
Structurizr DSLは、検証コマンドがあるので、Claude Codeの「書く→検証→直す」の往復に乗せやすい図の形式です。ただし検証が保証するのはDSLの成立までで、図が実装に合っているかは根拠コメントと人のレビューで担保します。まず対応表を作らせ、コンテナの線引きを人が決めてからDSLを書かせる順序が、手戻りを最も減らします。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。
- docs.structurizr.com/dsl/language
- docs.structurizr.com/validate
- docs.structurizr.com/export
- docs.structurizr.com/commands
- docs.structurizr.com/dsl/implied-relationships
- docs.structurizr.com/ai/dsl-generation
- c4model.com/abstractions/container
- code.claude.com/docs/en/hooks-guide
- code.claude.com/docs/en/permissions