# ADR-0010: wrangler*.toml 複数構成 + SSoT lint 戦略

- **Status:** Accepted
- **Date:** 2026-04 (multi-Worker 分離) / 2026-04-22 (shared-bindings.toml SSoT 導入) / 2026-04-30 (ADR 化) / 2026-05-11 (store-sync Worker skeleton 削除 — 3 split + legacy へ縮小)
- **Decision Drivers:** チャネル別 Worker 分離 (CPU 制限 / 障害ドメイン分離) / wrangler の include 非対応 / binding ID の一貫性確保 / 1 人運用での同期コスト最小化
- **Stakeholders:** dev@parky.co.jp (sole maintainer)

## Context

### 出発点

Parky の API は元々単一 Worker (`api/wrangler.toml`) で動いていたが、以下の理由で **Worker 単位の分離** が必要になった:

1. **CPU time 上限**: Cloudflare Workers の Paid プランで 30s CPU、startup time も isolate 単位。重い処理 (画像処理 / cron / store-sync) を分離したい
2. **障害ドメイン分離**: admin Worker のバグで public API が落ちるのを防ぎたい
3. **デプロイ単位の独立性**: store-sync を deploy するために public API を再起動したくない
4. **secrets / binding 分離**: marketing 系の機密 (X / Insta API key 等) を public worker に乗せない
5. **route 単位のスケール**: public のトラフィックが急増しても admin に影響しない

### Cloudflare Wrangler の制約

- `wrangler.toml` は **`@include` / `import` 非対応** (v4 時点)
- 各 Worker は独立した `name` / `routes` / `crons` / `[[env.dev.kv_namespaces]]` 等を持つ
- 複数 Worker で同じ binding ID (KV / Hyperdrive / R2) を共有する場合、ID を **手動で複数 toml にコピー** する必要

### 当初の問題

binding ID (例: `KV_CACHE` の `id = "1f88d955..."`) を 4 ファイルに複製していたため、ID 変更時に 1 ファイル更新漏れで「片方の Worker だけ古い DB を掴む」ドリフトリスクが発生。

## Decision

### 採用する 5 ファイル構成 (2026-05-11 縮小)

| ファイル | Worker 名 | 役割 | route |
|---------|----------|------|-------|
| `wrangler.toml` | `parky-api` | 旧 monolith Worker (legacy) | (廃止予定) |
| `wrangler.public.toml` | `parky-api-public` | end-user 向け API (mobile / web home) | `api.parky.co.jp/*` |
| `wrangler.admin.toml` | `parky-api-admin` | 管理者 API (admin portal) + 全 cron + 全 queue consumer (store-sync / fcm-dispatch / places-refresh / x-ai-generate) | `admin-api.parky.co.jp/*` + queue trigger |
| `wrangler.marketing.toml` | `parky-api-marketing` | marketing 系 API + cron (X / Instagram / newsletter) | `marketing-api.parky.co.jp/*` |
| `wrangler.shared-bindings.toml` | (deploy 対象外) | binding ID の SSoT (Single Source of Truth) | — |

> **2026-05-11 update:** store-sync 専用 Worker (`wrangler.store-sync.toml` / `parky-store-sync`) は
> Phase 1 skeleton 状態 (未 deploy) のまま 2 週間放置されていたため削除。queue consumer は
> admin Worker 内で従来通り稼働 (`wrangler.admin.toml` の `parky-store-sync-*` queue 配線)。
> Phase 2 (2026-Q3 想定) で再分離する場合は git 履歴から復元する。詳細:
> [api/docs/store-sync-worker-migration.md](../../api/docs/store-sync-worker-migration.md)

### shared-bindings.toml の役割

- **deploy しない設計メモ + lint 用参照元**
- `[shared.dev.*]` / `[shared.prod.*]` ブロックで dev / prod の binding ID を一元管理
- 新規 binding 追加時は **必ず先にこのファイルに追記** してから各 Worker 設定に展開
- 環境メタデータ (`account_id`, `compatibility_date`, `compatibility_flags`, `ai_gateway_base_url`) も集約

### 同期検査 (機械強制)

`scripts/check-wrangler-consistency.sh`:
- shared-bindings.toml の値と各 wrangler*.toml の `[[env.dev.*]]` / `[[env.prod.*]]` を比較
- 不一致を検出したら exit 1
- pre-push hook + CI で実行

### 各 Worker の責務分離

| Worker | 主要 endpoint | 主要 binding | crons / queue |
|--------|--------------|-------------|-------|
| public | `/v1/mobile/*`, `/v1/web/*`, `/v1/owner-public/*`, `/v1/webhooks/*` | HYPERDRIVE / KV_CACHE / R2 / RATE_LIMIT_USER | (なし) |
| admin | `/v1/admin/*` | + RATE_LIMIT_LOGIN / RATE_LIMIT_OTP / KV_IDEMPOTENCY / STORE_SYNC_QUEUE | warmer / Places refresh / X insights / X scheduled post / **store-sync queue consumer + DLQ + (Phase 2 で予定の) 週次 cron** |
| marketing | `/v1/marketing/*` | + INSTAGRAM_DB (D1) / INSTAGRAM_R2 | newsletter / x_post |

### prod 移行

- `wrangler.shared-bindings.toml` の `[shared.prod.*]` セクションに本番 ID を順次投入
- prod 環境用の Worker (`parky-api-public-prod` / `parky-api-admin-prod` 等) は未作成 (Phase 1 リリース直前で作成予定)
- 移行手順は memory `project_parky_cloudflare.md` 参照

## Consequences

### Positive

- 障害ドメイン分離: admin Worker のバグで public API が止まらない
- デプロイ独立性: store-sync の更新で他 Worker が影響を受けない
- secrets 分離: marketing API key を public worker に乗せない
- shared-bindings.toml の SSoT で ID ドリフトを防げる
- check-wrangler-consistency.sh で機械検査
- prod 移行時もこの構造を踏襲することで、dev / prod の差分が ID 部分のみに局所化

### Negative / Trade-offs

- ファイル数が 6 → 開発者が「どの toml にどう書くか?」を覚える必要
- shared-bindings.toml と各 toml の同期は (現時点で) 手動 → 機械生成スクリプト化までは負担
- CORS allowlist など `[[env.*.vars]]` まで shared-bindings.toml の対象に含まれていない (現状は手動同期)
- 新 Worker 追加時に shared-bindings.toml への追記漏れリスク
- Worker 間の Service Bindings (内部呼び出し) を使い始めると配線がさらに複雑化する
- prod 移行手順 (`wrangler kv namespace create --env prod` 等) が手作業

### Mitigations

- `wrangler.shared-bindings.toml` 冒頭コメントに運用ルール (ID 変更手順 / 新規 binding 追加手順) を明記
- check-wrangler-consistency.sh を pre-push + CI で必須化
- 将来的に `scripts/gen-wrangler.ts` で各 Worker toml を shared-bindings.toml + テンプレートから生成する CLI を導入予定 (ADR-0010 v2 として再検討)
- vars セクションも対象に含めるべく check-wrangler-consistency.sh を拡張済み（`[shared.dev.vars]` / `[shared.prod.vars]` を shared-bindings.toml に追加、`check_vars_section()` 関数で全チャネル toml を照合）
- Service Bindings 採用は別途 ADR を立てる方針

## Alternatives Considered

- **Alternative A: 単一 Worker 維持 (`wrangler.toml` のみ)**
  - 不採用理由: CPU 制限 + 障害ドメイン分離 + デプロイ単位独立性が満たせない。Phase 1 で MAU 1 万到達時に admin 操作が public トラフィックに圧迫される懸念

- **Alternative B: TOML を JS / TS で生成 (config-as-code)**
  - 不採用理由: Wrangler は TOML を直接読むのみ、生成スクリプトを通すと CI / ローカル開発で「実 TOML との同期ズレ」が頻発する可能性。将来の選択肢として保留 (Phase 2 で再検討)

- **Alternative C: Cloudflare Service Bindings + 1 routing Worker (Vercel Edge Functions 流)**
  - 不採用理由: 1 routing worker が SPOF になる、各 Worker 間の通信オーバヘッド、複雑度が増す。Phase 2 以降の規模なら検討

- **Alternative D: 各 Worker を別リポジトリに切る (microservices repo)**
  - 不採用理由: 1 人開発期にリポジトリ分割は管理コスト過大。Layer-First で同 repo 内の依存関係を ESLint で機械強制する方針 ([ADR-0005](./0005-layer-first-mechanical-enforcement.md)) と矛盾

- **Alternative E: shared-bindings.toml を deploy 対象に含める (Worker として動かす)**
  - 不採用理由: deploy しないことで「設定メモ」として明確化、誤デプロイを防げる

## References

- 関連 memory:
  - `C:/Users/Taiga/.claude/projects/e--Claude/memory/parky/project_parky_cloudflare.md` (Cloudflare 移行進捗)
  - `C:/Users/Taiga/.claude/projects/e--Claude/memory/parky/reference_parky_cloudflare.md` (Account / Worker 名 / API Token 詳細)
- 関連 code:
  - `e:/Claude/high-field/parky/api/wrangler.toml` (旧 monolith)
  - `e:/Claude/high-field/parky/api/wrangler.public.toml`
  - `e:/Claude/high-field/parky/api/wrangler.admin.toml`
  - `e:/Claude/high-field/parky/api/wrangler.marketing.toml`
  - `e:/Claude/high-field/parky/api/wrangler.shared-bindings.toml` (SSoT)
  - `e:/Claude/high-field/parky/scripts/check-wrangler-consistency.sh` (整合性検査)
  - `e:/Claude/high-field/parky/api/src/index.ts` / `index-public.ts` / `index-admin.ts` / `index-marketing.ts` (各 Worker のエントリ)
  - ~~`api/wrangler.store-sync.toml`~~ ~~`api/src/store-sync-worker/`~~ (2026-05-11 削除、git 履歴より復元可)
- 関連 docs:
  - 親 ADR: [ADR-0006 Cloudflare Workers Platform](./0006-cloudflare-workers-platform.md)
  - `../ops/secrets-rotation.md` (該当ドキュメントがあれば)

## Revisit Triggers

- Wrangler が `@include` / `import` をサポートした時 (TOML が物理 include 可能になれば SSoT 戦略を簡素化)
- Worker 数が 10 を超えて手動同期が破綻した時 (gen-wrangler.ts 化を強制実施)
- prod 移行で binding ID 同期事故が発生した時 (gen-wrangler.ts 化を強制実施)
- Cloudflare の Service Bindings / Smart Placement の挙動が変わり、構成見直しが必要になった時
- multi-region デプロイ (US / EU 別 Worker) を導入する時
- Phase 2 (ARR 1 億 / 開発者 5 人以上) でモノレポ → microservices 分割が必要になった時
