reserve-optimizer マルチテナントSaaS化(C案ハイブリッド型)— 設計仕様

  • 日付: 2026-10-08
  • 状態: 承認済み(brainstorming 3ラウンドMLR+自己点検を経てv3-final確定・2026-10-08 ふくけい承認)
  • レビュー正典: obsidian-ssot/00_SYSTEM/マルチLLMレビュー/2026-10-08_reserve-optimizer-C案マルチテナント設計レビュー/(r1=39指摘/r2=44/r3=33・累計116指摘・履歴の正典)
  • 決定事項サマリ: 目的=就活実績ファースト(1〜3院・収益副次)/ 営業経路=C1(CC資料+面談)+E1(ココナラ統合)+C2(スカウトメール)+E2補助(IG動画・デモ動画3経路共用)/ 価格=月3,000円一律(1院目から有料・Stripe決済は既存実装流用)/ 技術=C案ハイブリッド型・軽量中央ハイブリッド順序(M1・2026-10-08承認)

1. 背景

既存システムは単一テナントの整骨院向けLINE予約Bot(GAS+スプレッドシート+LINE Messaging API+Stripe+Cloudflare Workers・本体JS約12,700行・E2Eテスト36本緑・1院のみで実運用品質)。GitHub棚卸し(2026-10-08)で最有力事業化候補と判定された。r1で単一基盤完全マルチテナント(案B)は「12,700行横断改修+静かな混線(院間データ混入)リスク」により見送り、「予約処理は複製・管理だけ中央集約」の案C(ハイブリッド型)を採用。

実績計上のスケジュール逆算(正典§1・r3 GLM#7採用): 「実績計上の最小運用期間=3ヶ月×複数院」を就活開始時期から逆算する。期内にC3到達不可の場合のフォールバック=単院実績+設計プロセス語り(ADR+Zenn記事)。

設計正典とレビュー履歴: obsidian-ssot/00_SYSTEM/マルチLLMレビュー/2026-10-08_reserve-optimizer-C案マルチテナント設計レビュー/revised_proposal.md(v3-final)+ 同dir review_log.md(r1〜r3の差分履歴・指摘原文×裁定)。

2. 案の定義と判断記録(ADR要約)

案 内容 判定
A 複製のみ手動運用(中央なし) 工数最小だが語り・管理面が弱い。ロールバック先として保持
B 単一基盤完全マルチテナント(tenant_id全統合) 見送り(混線リスク+週20h枠で非現実的・r3 OR1#6)
C 本設計(複製+中央集約) 採用(2026-10-08・N=3院到達時に再評価)

詳細比較(中央を作る/作らない4軸・TCO表)はADRとして本specの実装時に docs/adr/ へ展開する。

3. アーキテクチャ

  • データプレーン: 院ごとにGAS複製+スプレッドシート複製+LINE Bot。現行コードを無変更で流用・E2E 36本を有効のまま維持
  • LINE webhook: 各院GAS直受け(中央非依存)+署名検証。署名検証失敗時=401即返+ボディ空+テナント単位ログ(30日ローテート)+イベントID冪等キー(GAS Cache 24h・LINEリトライ暴走防止)
  • コントロールプレーン(中央): Cloudflare Workers+D1。テナント登録簿(tenant_id/院名/GASデプロイURL/LINE channel ID/Stripe契約ID/状態)+稼働監視+Stripe請求+オンボーディング
  • D1: 日次エクスポート(RPO=24h・失敗検知はC2で毎時ステータスチェック)・RTO目標=4h+復元手順書+C3で復元訓練1回(失敗時代替=CSV→D1インポート)・Workers KV読取副本
  • テナント状態遷移: (a)中央稼働前=自律稼働 (b)ライセンス有効=通常 (c)TTL72h超過=警告(予約継続) (d)7日超過=人手提供停止判断
  • 個人情報: 中央登録簿は院名等の最小限のみ・予約データ(氏名・電話・来院履歴)は各院シートに閉じる(設計保証をアーティファクト化)
  • シークレット: 各院GASのScript Properties(漏洩影響半径=1院)+編集者=開発者のみ+ローテーション手順+C2で半自動化(GAS API経由)
  • 管理手段: 1〜3院ではWrangler CLI+定型操作手順書(フルUIは構築しない・N=3再評価事項・手順書配置=docs/runbooks/)
  • 「無変更流用」の定義(r4 Gemini#1反映): 予約処理のコアロジックは無変更・テナント固有の定数(シートID・channel ID等)はC0で外部化・パラメータ化する(変更は定数の参照経路のみ・ロジック変更を含まない)
  • 用語の分離(r4 GLM#7反映): 「LINEチャネル署名検証」(webhook・各院GAS)と「ライセンストークン検証fail-closed」(C3・中央との契約状態確認)は別機能として呼称を固定
  • Workers KV読取副本のスコープ(r4 Gemini#4反映): テナント登録簿の読み取り専用フィールドのみ・D1書込時にイベント駆動で同期・結果整合性(古い副本は警告状態へのフォールバックに使用)
  • D1エクスポート失敗検知の具体化(r4 Gemini#3反映): ①Workers Cronが毎時エクスポートジョブの実行結果ステータスをD1に記録 ②エクスポート先(R2)のオブジェクト存在チェック ③2仕掛けの不一致でアラート
  • 主要ストアの配置(r4統合反映):
データ 保存先 保持
冪等キー重複検知テーブル 中央D1(スキーマ=event_id/tenant_id/ts・C2で実装) 7日
署名・ライセンス再検証キュー 中央D1(event_id/tenant_id/ts) 30分超過で手動確認対象
テナント単位の署名失敗ログ 各院GAS(Cloud Logging) 30日
営業メール送信ログ 中央D1 email_log 3年(特電法)
ハートビートペイロード 中央D1 非PIIのみ(tenant_id/最終予約時刻等・r4 GLM#6反映)
工数WBS docs/wbs.md(git管理下) —
障害時コミュニケーション文面 中央提供テンプレ+テナント上書き可 —
  • テナント状態遷移の統合規則(r4 MiniMax#1/GLM#3/OR1#4反映): 優先順位は「自動停止(LINE障害)>人手提供停止(未入金×2回)>警告(TTL72h)」・未入金カウントは成功支払いでリセット・自動停止からの復旧権限=開発者(手動)・「unknown化」は状態(b)内のサブフラグ(死活不明・自動停止ではない)。状態遷移図は実装planで図化

4. 段階移行(軽量中央ハイブリッド順序・確定)

フェーズ 内容 ゲート
C0 grep定数掃出し(コード+E2Eテスト内tenant固定値) 定数リスト・流用可否判定+セキュリティ自己レビュー(OWASP Top10視点)+価値検証(模擬面接1回)
C1(軽量版) 登録簿D1+ハートビートのみ(Stripe/監視はC3) E2E36本全緑+MLR+KPI基準値確定+ハートビート源=日次定期トリガー方式の検証(合格基準=T0001で7日連続トリガー発火確認+発火失敗時の外部再作成手順の動作確認・r4 GLM#12反映)+中央自体の外部死活監視組込み(UptimeRobot等の無料外部プローブ→Workersヘルスエンドポイント・「中央が死んで誰が気づくか」対策・r4 GLM#2反映)
C2 複製オンボーディング半自動化+sandbox T0002実機 36本をT0002設定で全実行+プロパティ帰属検証+並列テスト設計(webhook順序不保証・replay帰属)+冪等キー独立ゲート(Stripe event.id・重複検知テーブル)+Stripe customer衝突シナリオ(metadataバインド+不一致即時拒否)+StripeテストキーE2E 1トランザクション(範囲=T0002向けテストモードで契約作成→webhook→登録簿stripe_id反映まで1件通し検証・本番課金はC3。合格条件=customer衝突シナリオ再現+不一致拒否確認+冪等重複検知動作・テスト/本番API差分リストを別途文書化・r4 GLM#4/OR1#2反映)+オンボーディング≤30分+デプロイ≤30分/院必須+部分デプロイ検知実測+ロールバックRunbook(所要時間をRTOに計上)+LINEチャンネル名義・引継ぎポリシー決定
C3前 実テナント1院のオンボーディング実測+分離検証ログ 実測ログ
C3 Stripe請求(metadataバインド+不一致即時拒否+冪等キー)+ライセンスチェック+監視本番化 署名検証fail-closed(失敗時=データ読取禁止+アラート+直近30分再検証キュー)+法務整備(利用規約・特商法・解約・返金・失効事前通知72h前)+fail-closed切替(未入金×2回)+障害時顧客コミュニケーション規定(テナントの患者を含む)+停止直前予約の院への引渡し+患者向け自動返信+GAS内部エラー・クォータの継続集約+Workers復旧後のD1-GAS間予約差分レポート自動生成

5. エラー処理・運用

  • bounded fail-open(ライセンスキャッシュTTL=72h・超過で警告+通知・7日超過で人手停止判断・未入金×2回で自動停止)
  • ハートビート: 実イベントベース+日次定期トリガー保証(C1で検証)
  • fail条件: LINE API 5xx/timeout 5分以内5回→予約受付自動停止+緊急アラート+Bot障害告知。復旧=自動再試行(1/5/15分)→再疎通で自動復旧→30分超過で手動
  • テナント側fail条件: Stripe webhook 24h不着→課金unknown化+手動確認・ハートビート24h不着→テナントunknown化・Properties欠損→起動拒否
  • 内部論理バグ検出のfail条件(r4 MiniMax#18反映): 同一患者IDが30分以内に3件以上の予約成功、または同一event_idの再処理が複数回検知→当該テナントを警告状態へ移行+開発者通知(重複処理の安全弁)
  • 自動停止からの復旧権限=開発者(手動)・「人手提供停止判断」との競合規則は§3状態遷移統合規則どおり
  • デプロイ: 深夜帯ルール+T0002先行適用→24h観察→本番+部分デプロイ検知(最終更新時刻pull差分)+バージョン照合ゲート化
  • CC実装の品質担保: 各フェーズ=リグレッション実行+CIログ保存

6. 営業資材(実装は本specの後続plan)

提案書・デモ動画(IG/面談/ココナラ3経路共用)・スカウトメール文面(特電法8要件+受信側テスト)・ココナラ出品文(個人名義限定を基本・Phase 0判定)・匿名Instagram運用方針・ADRのZenn外部発信

7. リスク

  • スケール閾値: オンボーディング30分/院超過またはGASクォータ逼迫(外挿=上界/平均2本立て)でB案移行再検討。クォータ逼迫時の即時アクション=有料院1院で課金枠確保→他院無料β保留(消費率80%超で判定)
  • GAS無料枠縮小・LINE API破壊的変更(変更ログ監視のみ・抽象化レイヤーは無変更流用と矛盾で不導入・破壊的変更検知時=本設計の再評価)
  • 反証シナリオ: ①中央2週ダウン→Workers復旧後D1-GAS差分レポート ②GAS無料枠縮小→課金枠確保アクション ③テナント側GAS障害→LINE標準応答メッセージで最低限維持
  • 最悪ケース(GoogleアカウントBAN): E2E・clasp設定・デプロイスクリプトのGitHub管理+各院Properties暗号化バックアップ(方式をC3実装planで1つに確定・r4 GLM#9反映・顧客合意付き)+スプレッドシート週次エクスポート+日次CSV匿名化を別事業者(R2等・候補・コスト・同期頻度はPhase 0調査タスク・r4 OR1#11反映)へ+BAN Playbook(24h以内告知・返金・Stripe webhook停止時の自動課金停止+顧客連絡)
  • 反証シナリオ④(r4 MiniMax#12/#16反映): 人為ミス経由の中央データ破壊(Wrangler CLI誤投入でtenantsテーブル上書き/KV誤purge)→ C3の復元訓練をこのシナリオで実施(最頻出ミス種別の訓練化)
  • 無償β保留時の患者影響(r4 OR1#16反映): β保留院の患者へのサービス制限通知テンプレートを法務整備に含める
  • 差分レポートとRTOの関係(r4 OR1#3反映): RTO=4hは「予約受付の復旧」まで・差分レポート生成はRTOに含めず別途SLA(復旧後24h以内)とする
  • 削除リクエスト対応(r4 OR1#5反映): 患者からのデータ削除要求の処理フロー(中央は非保持のため該当院へ転送→院がシートで対応)を法務整備の項目に含める
  • 反証①の実装可能性確定(r4 GLM#1/OR1#15反映): 中央長期ダウン時のデータ保全は各院GAS/シート側に留まり、中央復旧後に「D1エクスポート末尾時刻 vs 各院シート」を入力ソースとした差分レポートで整合確認(中央は代替経路を持たない・完全再算方式)

8. Phase 0(C0と並行実施可・C1着手前までに完了する測定・決定事項)

現行院メトリクス2〜4週(KPIベースライン)/予約成功率の集計実装(シートに成功/失敗イベント行記録+週次集計関数・r4 GLM#8反映)/GASクォータ消費率測定(webhookリトライ最悪値シナリオ含む)/適法性チェックリスト(Stripe名義・特商法・インボイス・兼業規定+ココナラ経路)/兼業確認「明確OK」がC3課金開始の前提(順序保証)/兼業NG時=無償β(撤退基準ルール同適用)+OSS化/T0001=既存院の課金扱いは無償据え置き/オプトイン課金化のいずれかをPhase 0で決定(オプトインならC3本線外の別ライン・r4 MiniMax#7反映)/T0001課金扱い決定/撤退基準(面談1件+往復2回=操作的定義・2週連続面談3件未達で撤退・自動カウント)/工数WBS(開発+営業6:4・0.5〜2h粒度・保管先=docs/wbs.md・2週連続超過でスコープ縮小。落とす順序= C3ダッシュボード→監視拡張→C2並列テスト簡略化・A案ロールバックは最終手段)/スケール外挿方法の事前決定/価値検証=模擬面接1回以上(CC資料初版の完成を前提タスクとする・可能なら複数回・r4 MiniMax#19/GLM#13反映)

9. KPI

予約成功率99%以上(分母=Bot受付リクエスト・システム起因失敗を除く)/ハートビート応答率99.5%以上+単一テナント連続欠落3回以下/検知遅延3h以内(超過継続=C3昇格保留+訓練追加)/課金処理KPI=失敗24h以内検知対処100%/設定取り違え0件(自己診断4項目全一致)/オンボーディング30分以下

10. ゲートの追加停止判断

MLR 3ラウンドで機械ゲートが7→13→20と単調増加したため、本spec確定以降は新規ゲートの追加を停止する(目的圧倒ドリフト監視・新規指摘は実装plan段階で処理)。

11. 面接語りのためのADR要点

「なぜB(完全マルチテナント)でなくC(複製+中央)か」= 混線リスクの構造的排除+週20h枠での完走可能性+E2E36本資産の維持。軽量中央順序=実績最速とSaaS語りに必要な中央実績の両立。判断日=2026-10-08・N=3院で案全体を再評価。


This site uses Just the Docs, a documentation theme for Jekyll.