第10章: ケーススタディ(仕様→設計→検証→AI実装)
最終章では、前章までに分解した概念を 1つの変更要求へ戻します。 仕様差分、AI 委任、レビュー、CI が、 どの順序で連鎖するかを通しで追います。
本章の見せ場は、CancelOrder の Context Pack diff と差し戻し表です。 抽象概念が PR 運用でどの観点に変わるかを、最後にまとめて回収します。
学習ゴール
- 1つの変更要求が、どの artifact と検証へ波及するかを追跡できる
- Context Pack 差分 → AI 委任 → レビュー → CI の順序を説明できる
- 第1〜9章で導入した概念が、実務ループのどこで効くかを回収できる
- 自案件へ移植するときに固定すべき最小成果物を判断できる
ケースの起点: 「出荷前の注文取消」を追加する
本章では、注文処理の共通例題に「支払い前の注文は取消できる」という変更要求を入れます。 ただし、取消と同時に監査証跡が残り、必要なら在庫引当が解放されることを必須にします。 返金、出荷後取消、配送事業者との連携はこの章の対象外です。
- Goal:
DraftまたはPlacedの注文をCancelledに遷移できるCancelOrder成功時に監査証跡が残るPlacedの注文を取り消した場合は在庫引当が解放される
- Non-goal:
Paid済み注文の返金フロー- 出荷後取消と配送取消
- BI/分析向けの取消理由集計
- 完了条件:
CancelOrderの契約、Diagram、Acceptance test が本文内で追える- AI 出力をどの根拠で差し戻すかが分かる
- CI が何を検知し、どこへ戻るかが分かる
この章で示すのは「コードの書き方」ではなく、「どの順序で契約を固定すれば壊れにくいか」です。
変更前の契約を読む
最初に見るのはコードではなく契約です。今回の論点は Order の状態と、
PlaceOrder 以降に発生する副作用です。
problem_statement:
goals:
- 注文の作成から出荷までの一連の処理を、境界と不変条件で記述できる
- 監査ログを「後付け」ではなく契約として扱う
non_goals:
- 決済事業者・配送事業者の具体実装の詳細化
objects:
- id: Order
states: [Draft, Placed, Paid, Shipped, Cancelled]
Cancelled は既に状態モデルに含まれています。したがって今回は状態追加ではなく、
「その状態へ至る操作」と「守るべき検証」を明示する変更です。
次に、変更が波及する既存操作を確認します。
- id: PlaceOrder
pre:
- Order.state == Draft
post:
- Order.state == Placed
- InventoryReservation が作成される(または更新される)
- AuditEvent("PlaceOrder") が記録される
- id: AuthorizePayment
pre:
- Order.state == Placed
- InventoryReservation.status == Reserved
post:
- Payment.status == Authorized
- Order.state == Paid
- AuditEvent("AuthorizePayment") が記録される
CancelOrder は PlaceOrder 後の在庫引当と、
AuthorizePayment 前後の境界に割り込みます。
この位置づけを曖昧にすると、支払い済み取消や監査漏れが混入します。
守るべき不変条件も先に確認します。
- id: D2-audit-consistency
statement: 重要操作(CreateOrder/PlaceOrder/AuthorizePayment/ShipOrder)は必ず監査証跡を残す
- id: D3-state-transition-safety
statement: 状態遷移は単調であり、禁止遷移(例: Shipped → Paid)は起きない
forbidden_changes:
- D2-audit-consistency を満たさない実装変更
- Order の状態モデル(states)を合意なく変更
全文は 共通例題: 注文処理 を参照してください。 本文では、読解に必要な断片だけを引き上げます。
Context Pack をどう変えるか
1) Morphism を追加する
最初に固定するのは CancelOrder の契約です。
ここが曖昧なまま AI に委任すると、
「汎用ステータス更新 API」や「監査を任意にする実装」が出やすくなります。
+ - id: CancelOrder
+ input: { orderId: "OrderId", reason: "string" }
+ output: { orderId: "OrderId" }
+ pre:
+ - Order.state in [Draft, Placed]
+ post:
+ - Order.state == Cancelled
+ - InventoryReservation.status == Released または対象が存在しない
+ - AuditEvent("CancelOrder") が記録される
+ failures:
+ - NotFound
+ - InvalidState
修正順が「Context Pack → 実装」である理由は明確です。 AI が触ってよい境界と、人間が差し戻す基準を先に固定するためです。
2) Diagram と Acceptance test を追加する
CancelOrder は操作追加だけでは足りません。
監査と在庫解放が守られるかを、Diagram と受入テストへ落とします。
- - id: D2-audit-consistency
- statement: 重要操作(CreateOrder/PlaceOrder/AuthorizePayment/ShipOrder)は必ず監査証跡を残す
+ - id: D2-audit-consistency
+ statement: 重要操作(CreateOrder/PlaceOrder/AuthorizePayment/CancelOrder/ShipOrder)は必ず監査証跡を残す
+
+ - id: D4-cancel-releases-reservation
+ statement: Placed の注文を取消すと、在庫引当は解放され、監査証跡が残る
+ verification:
+ - CancelOrder 成功後、Order.state == Cancelled になる
+ - InventoryReservation.status == Released になる
+ - AuditEvent("CancelOrder") が存在する
+ - id: AT4-cancel-before-payment
+ scenario: Placed の Order を CancelOrder する
+ expected:
+ - Order.state == Cancelled
+ - InventoryReservation.status == Released
+ - AuditEvent("CancelOrder") が存在する
+
+ - id: AT5-cancel-after-payment
+ scenario: Paid の Order を CancelOrder する
+ expected:
+ - InvalidState
この章では、例として D2 を更新し、D4 と AT4/AT5 を追加した差分を扱います。
既存契約を壊さず、変更要求の影響範囲だけを増やすのが要点です。実リポジトリの context-pack-v1.yaml の現在値そのものではなく、章内で読解に必要な差分を再掲しています。
AI へ委任する
Context Pack を更新したら、その差分だけを根拠付きで AI に渡します。 詳細な定型は 付録B: AIエージェント用プロンプト集 にあります。 この章では最小断片だけを再掲します。
以下の Context Pack 差分を唯一の仕様として、CancelOrder の実装スケルトンを作成せよ。
Forbidden changes を必ず守れ。
出力:
1. 実装構成案
2. CancelOrder に必要なモジュール境界
3. 未確定事項(質問)と、その理由
以下の Diagrams(D2, D4)を満たすことを検証するテスト観点を生成せよ。
仕様追加は禁止。Diagrams/Pre/Post/failures を変更してはいけない。
出力形式:
- Diagram id:
- 観測点:
- テスト粒度:
- 代表テストケース:
- 反例:
この時点で AI に期待するのは完成コードではありません。 まず必要なのは、境界の切り方と不足情報の洗い出しです。
- 実装構成案:
CancelOrderServiceを追加するOrderRepository、InventoryReservationRepository、AuditAppenderを使う
- テスト観点:
D2とD4に紐づく統合テストを提案するAT4とAT5に対応する成功/失敗ケースを提示する
- 期待する質問:
Paidの取消を返金付きで扱うか- 在庫解放を同期で行うか、非同期に切るか
質問が返ってこない場合は、AI が non-goal を勝手に補完している可能性があります。
レビューで差し戻す
AI 出力は「それらしく見える」だけでは通しません。
差し戻し理由は、必ず problem_statement、Diagrams、forbidden_changes にひも付けます。
| AI提案 | 差し戻す理由 | 根拠 |
|---|---|---|
UpdateOrderStatus という汎用 API にまとめる |
CancelOrder の pre/post が消え、禁止状態遷移が紛れ込む |
D3-state-transition-safety、CancelOrder.pre/post |
| 監査失敗時は warning のみで続行する | 監査を任意化すると運用要件を壊す | D2-audit-consistency、forbidden_changes |
Paid でも取消可能にして返金は後続対応にする |
仕様追加であり、non-goal を越える | problem_statement.non_goals |
レビューの順序も固定します。
- まず
Context Packと Diff を照合する - 次に
Diagram idごとのテスト観点が出ているかを見る - その後に実装詳細へ入る
この順序を守ると、レビューが感想戦になりません。
CI で何を検知するか
このリポジトリでは、文書 QA と Context Pack 検証を CI に組み込んでいます。
通しケースでは、どの失敗をどのゲートが拾うかを分けて理解することが重要です。
python3 scripts/validate-context-pack.py docs/examples/common-example/context-pack-v1.yaml- 必須項目不足、ID 重複、最小構造の崩れを検知する
python3 scripts/validate-context-pack-schema.py docs/examples/common-example/context-pack-v1.yamlContext Pack v1のスキーマ逸脱を検知する
npm run qa- リンク、構造、Unicode、textlint、rendered HTML を含む主要チェックをまとめて実行する
- Diagram / Acceptance test 由来のテスト(別リポジトリで動かす想定の例)
- 例題アプリ側の CI で、
D2、D4、AT4、AT5がコード差分で壊れていないかを検知する(この book リポジトリの.github/workflows/ci.ymlでは実行していない)
- 例題アプリ側の CI で、
CI が落ちたときの修正順も、ケーススタディの一部です。
- 契約とテスト期待が食い違うなら、先に
Context Packを見直す Context Packが正しいなら、実装を修正する- 文書 QA だけが落ちたなら、本文とリンクを独立に直す
局所的にコードだけ直すと、次の PR で同じ破綻が再発します。
このケースで前章の概念がどう効くか
| 章 | 概念 | このケースで効く箇所 |
|---|---|---|
| 第1章 | 責任分界と Context Pack | 人間が CancelOrder の契約を固定してから AI に渡す |
| 第2章 | Objects / Morphisms | Order と InventoryReservation を保ったまま、CancelOrder を明示的な Morphism として追加する |
| 第3章 | Diagrams | D2 と D4 をテスト観点へ落とし、監査漏れと在庫解放漏れを検知する |
| 第4章 | 関手 | 仕様差分を、境界を壊さず設計と実装へ写像する |
| 第5章 | 自然変換 | 差分レビューで「意味保存の変更か」を確認し、汎用 API 化を差し戻す |
| 第6章 | 普遍性 | 取消要求を万能 DTO にせず、最小契約として独立した操作に保つ |
| 第7章 | 統合図式 | 注文と在庫引当の整合を、統合境界の問題として扱う |
| 第8章 | 分業と配線 | 取消、在庫解放、監査追記を別責務として配線し、順序を明示する |
| 第9章 | 効果境界 | 状態判定は pure core に置き、監査追記と在庫解放は impure shell に閉じ込める |
ケーススタディの payoff は、この表をコードレビューの観点へそのまま持ち出せることです。
他案件へ移植するポイント
- 変更要求は 1 PR で 1 件に絞る
- 先に確認するのは
problem_statement、morphism、diagram、acceptance test、forbidden_changes - AI には全文コードではなく、契約差分と non-goal を先に渡す
- レビューコメントは Diagram id か Forbidden change を根拠に書く
- CI が落ちたら「契約 → 実装 → 文書」の順で戻る
この 5 点だけ守れば、対象ドメインが変わっても運用の骨格は再利用できます。
演習
演習1: 別の変更要求で同じ流れを再現する
RefundOrderやPartialShipmentのように、別の変更要求を 1 つ選ぶproblem_statementの Goal / Non-goal を書く- 追加または更新する
Morphism、Diagram、Acceptance testを 1 つずつ決める -
Context Pack の検証を通す
python3 -m pip install -r scripts/requirements-qa.txt # 初回のみ python3 scripts/validate-context-pack.py docs/examples/common-example/context-pack-v1.yaml python3 scripts/validate-context-pack-schema.py docs/examples/common-example/context-pack-v1.yaml npm run qa # 任意 - AI へ渡す prompt と、差し戻し条件を 3 行で書く
演習2: レビュー観点を PR コメントとして書く
次の 3 つを必ず含むレビューコメントを作成してください。
- どの
Diagram idを根拠にしているか - どの
Forbidden changeまたはNon-goalに触れるか - 直す順序が
Context Pack先行である理由
まとめ
- ケーススタディは、1つの変更要求を artifact の連鎖として追える形にすると価値が出る
- 先に固定するのはコードではなく、
Context Packの契約、Diagram、Acceptance test である - レビューは感想ではなく、
Non-goal、Diagram id、Forbidden changesを根拠に行う - CI が落ちたときは
Context Packから戻ると、局所修正で終わりにくい
次に使う導線
- 付録Aのテンプレと付録Bのプロンプトを使うと、この章の流れを自案件の PR に移せる。
- 仕様の現在値を引き直したいときは、共通例題と Context Pack v1 仕様へ戻ると差分を再現しやすい。