AIインスタ担当 v4 アーキテクチャ設計書(ADR 叩き台)

2026-04-17 | Claude Code 作成 | 子安氏レビュー・加筆用の叩き台

📝 本書の位置づけ

Codex 5回目レビュー(14)で「v3 は 100店舗 SaaS として ship 不可、v4 を ADR/prototype から始めるべき」と結論された。

本書は CC(Claude Code)が作成した v4 設計の叩き台。子安氏にゼロから依頼するのではなく、叩き台をベースに具体的な議論を進めることで、子安氏の工数を圧縮しつつ方向性のズレを早く検出する目的で作成。

レビュー観点: 本書のすべての前提・判断・数値・代替案は子安氏の批判対象。最終的な ADR は子安氏加筆後に確定する。

📑 目次

1. Context — なぜ v4 なのか

1.1 v3 での失敗の要約

ラウンドCodex指摘結果
1回目scheduler/approval/cron/version 5件🟢 修正済み
2回目スケール + セキュリティ 11件🟡 部分対応
3回目retry race / token / upload / audit / limit 5件🟡 方向性OK・実装瑕疵
4回目audit再発 / migration / limit / upload 4件🔴 未解決
5回目アーキテクチャ判定🔴 No-ship for 100-store

1.2 Codex 5回目の核心結論

「v3 は、それらを提供しないプリミティブとフローから、atomicity / idempotency / authenticated authority / bounded memory / operational recovery を合成しようとしているため、修正するたびに同じ欠損 invariant が別サブシステムで再発する」

→ v3 のアーキテクチャ選択(Workers + KV + D1 中心)は 100店舗商用 SaaS の要件と構造的にミスマッチ。

1.3 v4 のスコープ

2. Invariants — 非妥協事項(10項目)

Codex 5回目(5.1 セクション)で「root cause を invariants として reframe せよ」と指摘。以下はその 10項目。v4 のいかなる案もこの 10項目を満たすことを前提とする

INV-1Authenticated Actor Model MUST

すべての副作用 API は認証された actor を持つ。匿名 bearer URL で Instagram 公開が起きてはならない。

v3 の失敗: 承認 URL token 単独で publish 起動可能だった(confirm_token を後付けしたが根本解決せず)

INV-2Tenant Isolation MUST

店舗(テナント)間のデータ・レート制限・監視イベントが絶対に混ざらない。クエリ・ストレージ・キャッシュのすべてで store_id 境界が機械的に enforce される。

INV-3Idempotent Instagram Publish MUST

予約投稿が Worker クラッシュ・タイムアウト・IG API 一時障害に遭っても、絶対に二重公開されない。成功・失敗・不明のどの状態からも reconciliation 可能。

v3 の失敗: ロック解放と外部 API 成功の間に window があり、重複投稿リスクが残存

INV-4Bounded Media Path MUST

画像処理は Worker isolate のメモリ上限(128MB)を絶対に超えない。streaming / object storage 経由のアップロードが標準フロー。

v3 の失敗: 20MB cap でも formData() parse が先に走りバッファリング

INV-5Durable Audit Trail MUST

承認・公開・設定変更のすべてに tamper-evident な監査証跡が残る。並行書き込みで chain が fork しない。

v3 の失敗: 4ラウンド修正後も audit chain が並行書き込みで fork する

INV-6Single Writer Where Required MUST

rate limit counter / audit chain head / publish state machine など「single writer が必要な箇所」で本物の atomic single writer が存在する。KV の read-then-write パターンで代用しない。

v3 の失敗: KV を atomic counter として使い race が毎ラウンド発生

INV-7Observable Operations MUST

投稿失敗 / Cron 遅延 / 認証攻撃 / audit 異常 がリアルタイムで検知され、担当者に通知される。Postmortem 可能なログが 90日保全される。

INV-8Recovery from Partial Failure SHOULD

「Instagram には公開されたが DB には記録されていない」「DB には公開済みだが Instagram 側は未公開」などの partial failure を検出し、手動 or 自動で reconcile できる仕組み。RTO 1時間、RPO 5分。

INV-9Abuse Controls SHOULD

Credential stuffing / 承認 URL 漏洩 / 画像 upload DoS / Cron retry storm などの攻撃に対する多層防御(WAF + Turnstile + atomic rate limit + quota)。

INV-10Migration Rollback SHOULD

v4 リリース時に v2 → v4 の段階移行ができ、問題発生時は v2 へ戻せる。店舗単位で v2/v4 を切り替え可能。

3. Load Envelope — 負荷エンベロープとGo/No-Goゲート

Codex 5回目(5.3 セクション)で「store 数ではなく load envelope で判断せよ」と指摘。100店舗契約時の想定ピーク値を以下で定義する。

3.1 定常時の負荷

指標想定値備考
店舗数100契約上限
1店舗あたり月間投稿数30〜90平均 60
全体月間投稿数3,000〜9,000平均 6,000
全体日次投稿数100〜300平均 200
全体ログイン/日500〜1,000店舗あたり 5〜10 回
Audit event/日3,000〜6,000投稿・ログイン・設定変更の合計

3.2 ピーク時の負荷(Go/No-Go ゲート)

以下のピーク値を 各ゲートとして定義し、v4 アーキテクチャはこれを全て満たすことを検証する(実装前に prototype 負荷試験)。

ゲートID指標目標値v3 での達成度
G1同時アップロード(リクエスト並列度)50 req/分🔴 128MB isolate 超過リスク
G2同分予約投稿バースト50 件/分🔴 LIMIT 5 polling で詰まる
G3Instagram API 同時コール100 req/5分🟡 未計測
G4ログイン攻撃時のPBKDF2並列度10 fan-out 以下🔴 並列 race で fan-out 無制限
G5Audit 並行書き込み20 write/秒 で chain 整合維持🔴 並行で fork する
G6retry storm 制御IG outage 1時間後に全件 5分以内で復旧🔴 retry backoff 5分/15分固定
G7recovery RTO1時間以内🟡 未定義
G8recovery RPO5分以内(データ喪失上限)🟡 未定義
G9テナント分離テスト100%(他店舗データ参照不能)🟢 v2 時点で担保
G10本番マイグレーション所要1店舗あたり 10分以内で v2→v4 切替可🟡 未設計

4. 案A: Cloudflare-native(DO+Queues+R2+D1)

4.1 構成図

┌─ Cloudflare Pages ─────────── フロントエンド(React+Vite)
│
├─ Cloudflare Workers ───────── fetch: auth / API routing / signed URL 発行
│    ├─ JWT verify / session
│    └─ 軽量ロジックのみ(副作用は DO/Queue へ)
│
├─ Durable Objects(DO)
│    ├─ PublishOrchestratorDO(per-post)
│    │    - publish state machine(outbox pattern)
│    │    - fencing token 保持
│    │    - Instagram API 照合 reconciliation
│    ├─ RateLimitDO(per-IP)
│    │    - atomic counter(PBKDF2 前の admission gate)
│    │    - login / API / 承認の統合 abuse control
│    └─ AuditAppenderDO(per-tenant)  ← Codex指摘で singleton 回避
│         - hash chain head 保持
│         - atomic append
│
├─ Cloudflare Queues ────────── 予約投稿 / retry / 非同期処理
│    - Cron Trigger は enqueue のみ
│    - Queue consumer → PublishOrchestratorDO
│
├─ Cloudflare R2 ────────────── 画像 object storage
│    - Worker は signed URL 発行のみ
│    - 画像 base64 化は行わない
│
├─ Cloudflare D1 ────────────── トランザクショナルメタデータ
│    - stores / posts / approval_requests
│    - atomic counter / chain head は DO へ
│
├─ Cloudflare WAF ───────────── /auth/login, /approval の rate limit 外層
├─ Cloudflare Turnstile ────── 公開エンドポイントの bot/スパム対策
└─ Cloudflare Logpush → R2 ─── 90日監査ログ保全

4.2 Invariants との対応

INV案A での実現手段
INV-1 authenticated actor承認者は Cloudflare Access or 独自セッション(店舗アカウント+承認者サブアカウント)
INV-2 tenant isolationD1 行レベル + DO は per-tenant namespace
INV-3 idempotent publishPublishOrchestratorDO が outbox + fencing token + Graph API reconciliation
INV-4 bounded mediaR2 direct upload(client → R2 signed URL)、Worker は base64 触れない
INV-5 durable auditper-tenant AuditAppenderDO で atomic append
INV-6 single writerRateLimitDO / AuditAppenderDO / PublishOrchestratorDO で atomic
INV-7 observableLogpush + Sentry + Slack Webhook(06_v3運用監視設計 を流用)
INV-8 recoveryQueues DLQ + 手動 reconciliation script + IG API ID照合
INV-9 abuseWAF + Turnstile + RateLimitDO の多層
INV-10 migrationv2/v4 両立運用可(Cloudflare だけで完結、DNS切替不要)

4.3 メリット

4.4 デメリット・リスク

5. 案B: Postgres-based(Supabase or Fly.io + Postgres + Redis)

5.1 構成図

┌─ Vercel or Cloudflare Pages ── フロントエンド(React+Vite)
│
├─ Fly.io or Railway container ─ アプリケーションサーバー(Node.js + Hono)
│    or Supabase Edge Functions
│    ├─ JWT verify / session / CSRF
│    ├─ 業務ロジック(副作用含む)
│    ├─ Postgres advisory lock(publish state machine の fencing)
│    └─ Redis(rate limit counter / approval confirm token)
│
├─ PostgreSQL(Supabase or Neon or Fly Postgres)
│    ├─ stores / posts / approval_requests(RLS でテナント分離)
│    ├─ audit_logs(advisory lock で single writer 強制)
│    └─ publish_outbox(トランザクショナル idempotency)
│
├─ Redis(Upstash or Fly Redis)
│    - atomic counter(INCR/DECR でrate limit)
│    - 短期キャッシュ / session blocklist
│
├─ Object storage(Supabase Storage or Cloudflare R2 or S3)
│    - client → presigned URL で direct upload
│
├─ Job queue(pg-boss on Postgres or BullMQ on Redis)
│    - 予約投稿 / retry / reconciliation
│
├─ Cloudflare WAF(DNS前段)
└─ Sentry / Better Stack / Datadog for observability

5.2 Invariants との対応

INV案B での実現手段
INV-1 authenticated actorSupabase Auth or Clerk or Auth.js(session-based、CSRF token標準装備)
INV-2 tenant isolationPostgres RLS(Row Level Security)で機械的に enforce
INV-3 idempotent publishpublish_outbox テーブル + advisory lock + Graph API 照合
INV-4 bounded mediapresigned URL で client→object storage direct
INV-5 durable auditPostgres トランザクション内で audit_logs append(hash chain)
INV-6 single writerPostgres advisory lock / Redis atomic INCR
INV-7 observableSentry + Better Stack(構造化ログ・アラート)
INV-8 recoverypg-boss retry + DLQ + Postgres point-in-time recovery
INV-9 abuseCloudflare WAF + Turnstile + Redis atomic counter
INV-10 migration店舗単位でDNS/アプリ切替(FF付き段階移行)

5.3 メリット

5.4 デメリット・リスク

6. コスト比較(100店舗規模)

6.1 月額コスト推定

項目案A: Cloudflare-native案B: Postgres-based
コンピュートWorkers Paid $5 + DO requests ~$5Fly.io container 2×VM $30 or Supabase Pro $25
データベースD1 無料枠内 or $5Supabase Pro込み or Neon $19
QueueQueues ~$1pg-boss(DB同居・追加費用なし)or Upstash Redis $10
オブジェクトストレージR2 $1〜2Supabase Storage込み or R2 $1〜2 or S3 $2
Rate limit / counterDO 内蔵(追加費用なし)Redis Upstash $10 or Redis Fly $0
WAF / TurnstileCloudflare WAF $20 + Turnstile 無料Cloudflare WAF $20 + Turnstile 無料
Logpush$5Better Stack $15
Sentry$26$26
Claude API$30〜50$30〜50
合計約 $95〜120/月約 $155〜200/月

売上 ¥3.5M/月(¥35,000 × 100店舗)に対し、案A 粗利 96%、案B 粗利 94%。コストは判断基準にならない(両案とも十分安い)。

6.2 実装工数推定

フェーズ案A案B
ADR 確定 + 小 prototype16〜24h16〜24h
コア実装(auth / publish / approval / upload / audit)80〜120h60〜100h(Postgresパターンの定番)
運用監視(Sentry / Logpush / Slack)16〜24h16〜24h
移行スクリプト(v2→v4)16〜24h24〜40h(Postgresデータ形式変換)
負荷試験 + 本番 hardening24〜40h24〜40h
合計152〜232h(約3〜4ヶ月)140〜228h(約3〜4ヶ月)

7. 案比較と CC の推奨

⚠ この章は CC の仮見解で、子安氏の判断で覆される前提

7.1 観点別比較

観点案A案B勝者
invariants 全 10項目を自然に満たすB(Postgresの方が自然)
100店舗規模での性能余裕A(エッジ実行)
プラットフォームロックイン回避B
採用エンジニア確保△(DO経験者限定)◎(Postgres+Node.js標準)B
運用監視ツールの成熟度B
月額コストA(差は小)
既存 v2/v3 コードの流用△(Node移植)A
子安氏の実装経験不明不明要ヒアリング
プロダクション prior art少ない多いB
デバッグ・観測の容易さ△(DO中の状態追跡が困難)◎(SQL で可視化可)B

7.2 CC の仮推奨:案B(Postgres-based)

理由:

  1. Postgres + Redis + object storage は 30年の業界標準パターンで、invariants を自然に表現できる
  2. 採用・運用・移植の観点で長期的にリスクが低い
  3. Codex 5回目で「1つの transactional database が many invariants を表現できる」と明示
  4. Cloudflare ロックインを避けることで将来の選択肢が残る

7.3 ただし決定前に確認すべき点

8. 移行計画(v2 運用継続 + v4 並行開発)

8.1 タイムライン

Week 1-2   ADR 確定(子安氏レビュー+加筆)+ 負荷envelope 再検証
Week 3     小prototype(両案で publish/audit の重要パスのみ実装)+ 負荷試験
Week 4     案確定 + 実装スコープ詳細化
Week 5-16  v4 実装(約3ヶ月)
Week 17-18 本番負荷試験 + セキュリティレビュー
Week 19-20 移行(店舗単位で段階カットオーバー、v2 は同時稼働)
Week 21+   v4 のみで運用、v2 は retention 期間後に sunset

8.2 v2 の位置づけ(移行期間中)

項目運用方針
稼働店舗数30店舗までに cap(契約条件に明記)
機能追加凍結(クリティカルバグ修正のみ)
v3 コードfreeze(リポジトリに参考資料として保存、ship しない)
新規契約v4 リリースまで停止 or 30店舗 cap 契約のみ

8.3 移行時のリスクと対応

リスク対応
v2 → v4 データ移行ミス店舗単位段階移行、移行前後で投稿履歴を比較検証
v4 で未知のバグ発生feature flag で v2 にロールバック可能に設計
移行中の店舗体験悪化移行日を店舗と合意、深夜帯実施、48時間以内に完了保証
契約上の SLA 違反v2 期間中は「10〜30店舗の実績ベース」で SLA 設定、v4 以降に本契約SLA

9. リスクと対応

リスク発生確率影響対応策
ADR で案が決まらず時間浪費Week 2 末までに案決定、無理なら CC 推奨通り案B で進める
v4 実装が 3ヶ月を超えるスコープ削減(approval LINE通知・F-04 フィードバック学習は Phase 2 へ)
Instagram Graph API 仕様変更abstraction layer で隔離、v4 のコア設計は API 非依存に
子安氏の工数確保困難他エンジニアとの協業、CC が scoped 実装で支援
100店舗営業が想定より遅いv4 完成時に 30店舗運用継続、段階的に拡大
v2 が 30店舗 cap の間に事故06運用監視設計 を v2 にも適用(8〜12h で実装可能)

10. 子安氏への依頼事項

📝 このセクションが本書の本題

ここまでは叩き台。以下の項目について子安氏の判断・加筆をお願いしたい。

10.1 Must(ADR 確定前に必須)

  1. Invariants(2章)10項目の適切性: 足りない / 過剰 / 表現の揺れ / 優先度見直し
  2. Load envelope(3章)10指標の妥当性: 実際の運用経験から現実的な上限を再定義
  3. 案選定: 案A / 案B / 第3案 のどれを採用するか、決定根拠と共に
  4. 勝負所のprototype: time-boxed で実装検証すべき重要パスの特定(案選定と並行で)

10.2 Should(ADR確定直後に)

  1. 実装スコープの優先順位: Must(Phase 1)/ Should(Phase 2)の再定義
  2. 工数再見積: 子安氏の実装速度で現実的なタイムライン調整
  3. テスト戦略: 単体 / 結合 / 負荷試験 / カオス試験の手段と範囲
  4. デプロイパイプライン: CI/CD / feature flag / カナリアリリースの仕組み

10.3 Nice to have

  1. 移行ツール設計: v2 → v4 データ移行の具体手順
  2. ドキュメント構造: Runbook / ADR / API spec の管理方針

10.4 進め方の提案

10.5 スケジュール目安

日付マイルストーン
Day 0(今日)本書送付 + 初回 MTG 日程調整
Day 3〜7子安氏 事前レビュー
Day 7〜10初回 MTG(invariants / load envelope / 案選定)
Day 10〜17small prototype(両案 or 選定案のみ)
Day 17〜21ADR 確定 + Codex による ADR adversarial review
Day 21〜v4 実装開始

11. 付録:関連ドキュメント