SALON BOARD クローン — 親SRS(System Requirements Specification)
SALON BOARD クローン — 親SRS(System Requirements Specification)
Section titled “SALON BOARD クローン — 親SRS(System Requirements Specification)”Document ID: SRS-ROOT-001 Version: 0.3 Status: Draft Last Updated: 2026-04-25 Owner: yudai
0. このドキュメントの役割
Section titled “0. このドキュメントの役割”本書は親SRS。実装前に各機能単位で作成する子SRS(docs/SRS/features/*.md)の上位規範として機能する。
子SRSは以下を定義する:
- 個別機能のユースケース
- 画面仕様・API仕様(zodスキーマレベル)
- 状態遷移・業務ルール
- 受け入れ基準(テストケース)
親SRSは子SRS全体で共通するルール・制約・契約を定義し、各子SRSでの再記述を不要にする。各子SRSの冒頭で「本書はSRS-ROOT-001に従う」と明記する。
1. ドキュメント管理
Section titled “1. ドキュメント管理”1.1 対象読者
Section titled “1.1 対象読者”- 実装者(自分・将来の協力者)
- レビュー時の自己チェック
- LLMに実装を依頼する際のコンテキスト
1.2 変更管理
Section titled “1.2 変更管理”- バージョンはSemVer風:
Major.Minor(Majorはアーキテクチャ変更、Minorは要件追加) - 変更は必ず理由を変更履歴に残す
- 子SRS作成時、親SRSの更新が必要なら先に親を改訂してから子を書く
1.3 関連ドキュメント
Section titled “1.3 関連ドキュメント”| ID | タイトル | 場所 |
|---|---|---|
| DOC-ANALYSIS-001 | SALON BOARDクローン開発 基礎情報 | /outputs/SALON_BOARD_クローン開発_基礎情報.md |
| DOC-ERD-001 | ERD(Mermaid) | /outputs/SALON_BOARD_ERD.md |
| DOC-DIR-001 | ディレクトリ構成 | /outputs/SALON_BOARD_DIRECTORY_STRUCTURE.md |
| DOC-SETUP-001 | セットアップガイド | /outputs/SALON_BOARD_SETUP_GUIDE.md |
2. 用語集
Section titled “2. 用語集”| 用語 | 定義 |
|---|---|
| 店舗(Store) | 本システムのマルチテナント単位。1オペレーターは複数店舗に所属しうる |
| オペレーター(Operator) | 管理画面を操作するユーザー。サロンオーナー・店長・スタッフを包含 |
| 顧客(Customer) | 来店者・予約者。来店履歴・予約・スマートペイ対象 |
| スタッフ(Staff) | 施術担当者。オペレーターと一対一とは限らない(受付専業スタッフ等) |
| 予約(Reservation) | 来店予定。11状態を持つ状態機械 |
| メニュー(Menu) | 提供サービス。所要時間・料金・担当可能スタッフを持つ |
| レジ(Register) | 会計処理。現金・クレジット・電子マネー・スマートペイを束ねる |
| スマートペイ(Smart Pay) | HPB連携の事前決済・オンライン決済 |
| 掲載(Listing) | HPBに出す情報。サロン情報・メニュー・写真・クーポン等 |
| LIFF | LINE Front-end Framework。LINE内で動く顧客向けWebアプリ |
| MCP | Model Context Protocol。LLMとツールサーバーの標準プロトコル |
| RLS | Row-Level Security。Postgresの行単位アクセス制御 |
3. システム概要
Section titled “3. システム概要”3.1 目的
Section titled “3.1 目的”予約・顧客管理・会計・掲載を統合した美容サロン向けSaaS。SALON BOARDを参考にしながら、価格破壊・UX刷新・AIネイティブ・HPB非依存の4軸で差別化する。
3.2 ビジネス上の位置付け
Section titled “3.2 ビジネス上の位置付け”- SALON BOARD:HPB(ホットペッパービューティー)と一体提供、実質 ¥50,000/月水準
- 本プロダクト:¥10,000〜¥15,000/月水準を目指す、HPB非依存の自社顧客チャネル(LIFF)を内蔵
- AI差別化:MCPサーバー経由でClaude/ChatGPTから運営操作が可能
3.3 スコープ
Section titled “3.3 スコープ”In-scope(Phase 1〜4)
- 予約管理(作成・編集・キャンセル・状態管理・カレンダー)
- 顧客管理(基本情報・来店履歴・タグ・メモ)
- レジ・会計(現金・クレジット・電子マネー・スマートペイ)
- メッセージング(メール・LINE・SMS)
- スタッフ・設備・メニュー・営業時間管理
- 売上集計・日報・月次レポート
- HPB掲載管理(サロン・メニュー・写真・クーポン・ブログ・口コミ返信)
- LIFF顧客アプリ(予約・会員証・履歴)
- MCPサーバー(OAuth2.1経由)
Out-of-scope(本プロダクトで扱わない)
- 物販EC(美容院のEC販売)— Phase 5以降の候補
- 勤怠・給与計算 — 別SaaSと連携想定
- ネイル・エステ業態特化機能 — 美容室・理容室にフォーカス
- HPB”内部連携”(Recruit側API)— 一方向コンテンツ入稿のみ、予約同期はユーザー手動
- ネイティブアプリ(iOS/Android)— PWA + LIFFで代替、判断は別途
4. ステークホルダー / ペルソナ
Section titled “4. ステークホルダー / ペルソナ”4.1 オペレーター
Section titled “4.1 オペレーター”| ペルソナ | 特徴 | 主な関心 |
|---|---|---|
| サロンオーナー | 複数店舗経営、PC/スマホ両使い | 売上・会員・店舗横断の数値 |
| 店長 | 1店舗を運営、シフト管理 | 予約・顧客・スタッフ |
| スタイリスト | 自分の予約・顧客のみ閲覧 | 自分のスケジュール、担当顧客 |
| 受付 | 予約入力・会計業務 | カレンダー、レジ |
権限モデル:permission-based + プリセット role(RBACファサード)。詳細は §7.7。
- プリセット role(Phase 1 で UI公開):
owner:全店舗・全機能manager:担当店舗の全機能staff:担当店舗の予約・顧客読取+自分関連の書込receptionist:担当店舗の予約・レジ書込
- 内部は permission キー(
{scope}:{resource}:{action})で制御、プリセットは permission の束として表現 - 個別 override(
operator_permission_override)はスキーマに存在するが Phase 1 で UI非公開(feature flag off)
4.2 顧客
Section titled “4.2 顧客”- 20〜50代女性が中心(美容室の一般的な客層)
- LINE利用率が高い(LIFFが効く前提)
- 予約チャネルは HPB / 電話 / LINE / 店頭 の4系統
5. バウンデッドコンテキスト
Section titled “5. バウンデッドコンテキスト”ERD(DOC-ERD-001)と同じ8分割を採用する。子SRSは原則いずれかのコンテキストに属する。
| ID | コンテキスト | 主要エンティティ |
|---|---|---|
| BC-TEN | テナンシー・認証 | Store, Operator, Session, Passkey, TOTP |
| BC-MST | マスター・リソース | Staff, Equipment, Menu, BusinessHours, Shift |
| BC-CUS | 顧客・CRM | Customer, Note, Tag |
| BC-RES | 予約・会計 | Reservation, ReservationMenu, Event, Visit |
| BC-REG | レジ | RegisterClosing, CashMovement |
| BC-MSG | メッセージング | Message, AutoMessageRule, Coupon |
| BC-PAY | スマートペイ・精算 | SmartPayTxn, PointLedger, Settlement |
| BC-LST | HPB掲載 | SalonListing, MenuListing, Photo, Blog, Review |
6. 非機能要件(NFR)
Section titled “6. 非機能要件(NFR)”6.1 性能
Section titled “6.1 性能”- API p95:通常操作 300ms以下、重い集計 1s以下
- カレンダー初期表示:100予約程度の店舗で 1s以下
- 同時接続:1店舗あたり 10オペレーター同時操作を想定、全体で 1,000オペレーター同時
- ジョブキュー遅延:通常 5秒以内に処理開始、最大 30秒
6.2 可用性
Section titled “6.2 可用性”- 稼働率目標:Phase 3までは 99.5%、Phase 4以降 99.9%
- メンテナンスウィンドウ:月1回、深夜3〜5時(事前告知)
- 計画停止なしのデプロイ:rolling update / blue-green は Phase 3以降
6.3 セキュリティ
Section titled “6.3 セキュリティ”- 通信:HTTPS必須(TLS 1.2以上)
- 認証:オペレーター=Passkey必須 + TOTPオプション、顧客=LINE Login
- 認可:RLS + アプリ層RBAC二重ガード
- 監査ログ:書込系操作は全て
operator_action_logに記録、1年保持 - パスワード:自前パスワード運用は原則しない(Passkey強制)、緊急復旧のみメール+TOTP
- セッション:JWTは不使用、サーバーサイドセッション(better-auth標準)
- CSRF:same-site cookie + カスタムヘッダー検証
- 脆弱性対応:
bun pm audit(ornpm audit)をCIで実行、Critical/High は24h以内に対応
6.4 プライバシー・コンプライアンス
Section titled “6.4 プライバシー・コンプライアンス”- 個人情報保護法:顧客データは店舗が管理者、本サービスは委託先として位置付け
- GDPR:現時点は非対象(国内限定)、海外展開時再検討
- 特定商取引法:決済画面に事業者情報表示必須(加盟店=店舗)
- 景品表示法:クーポン・割引表記は店舗責任だがUI上で注意喚起を出す
- データ削除:顧客削除要求は論理削除 + 90日後物理削除のジョブ
6.5 可観測性
Section titled “6.5 可観測性”- ログ:構造化JSON(pino)、stdout → docker logs → fluent-bit集約(Phase 3〜)
- メトリクス:Phase 1は最小限(
/healthz+ DBコネクション数)、Phase 3でPrometheus - トレース:OpenTelemetry、Phase 2以降
- エラートラッキング:Sentry(無料プラン)を Phase 1から
- アラート:pager不要、Discord/Slack Webhookで通知
6.6 国際化
Section titled “6.6 国際化”- 言語:日本語のみ(Phase 1〜4)
- タイムゾーン:
Asia/Tokyo固定(DBはtimestamptz、表示時に変換) - 通貨:JPY整数固定(小数なし、税抜・税込の両方を別列で保持)
7. 共通制約(すべての子SRSに適用)
Section titled “7. 共通制約(すべての子SRSに適用)”子SRSはこの章に書かれたルールをすべて満たさなければならない。違反する場合は親SRSの改訂を伴う。
7.1 マルチテナント規律
Section titled “7.1 マルチテナント規律”- 全テーブルに
store_id列を置く(例外なし、無関係テーブルは作らない) - 外部キーは複合FK:
(store_id, target_id) → target(store_id, id) - RLSはPhase 1の最初のマイグレーションから有効化(後入れではなく最初から):
ALTER TABLE xxx ENABLE ROW LEVEL SECURITY;ALTER TABLE xxx FORCE ROW LEVEL SECURITY; -- ★ 所有者にも強制CREATE POLICY tenant_isolation ON xxxUSING (store_id = current_setting('app.current_store_id'));
- DB接続を2本に分離:
postgres(superuser):マイグレーション・管理用途のみ、アプリは絶対に使わないapp(非superuser):アプリ/ワーカーはこちらで接続、RLSが必ず適用される
- リクエスト毎に
SET LOCAL app.current_store_id:Hono middlewareで統一実装 - リポジトリAPIは
storeIdを必須引数:型で強制 - ジョブペイロードに
storeIdを必ず含む:ワーカー側タスク起動時にSET LOCALする - テナント跨ぎクエリ禁止:集計等は専用SQL(superuser権限)で、アプリ層からは触れない
DISABLE ROW LEVEL SECURITY禁止(レビューでブロック、CIでgrep検出)
7.2 識別子(混成戦略)
Section titled “7.2 識別子(混成戦略)”原則:UUIDv7(Postgres uuid 型、16バイト)を主キーの既定とする。一部の内部専用テーブルのみ bigserial を許容する。
7.2.1 UUIDv7を使う(原則)
Section titled “7.2.1 UUIDv7を使う(原則)”- URL・APIレスポンスに出る可能性がある
- 件数・IDが業務KPI(売上・流量・顧客数・店舗数)を示唆する
- テナント跨ぎ、マイグレーション、移管、復元の可能性がある
- 対象例:
store,operator,customer,reservation,visit,visit_line,register_closing,message,smart_pay_txn,coupon,menu,staff,equipment等の業務実体全般
実装
- Postgres
uuid型を使用(16バイト、native比較) - ID生成はアプリ層(
uuidv7()ライブラリ) - DBの
DEFAULT gen_random_uuid()は使わない(v4になってしまうため)。ただしPostgres 18 native v7 gen入ったら再検討
7.2.2 bigserial を許容する例外
Section titled “7.2.2 bigserial を許容する例外”以下の条件をすべて満たすテーブルに限り bigserial を認める:
- 外部(API/URL)に絶対に出ない内部専用
- 件数・ID値が漏れても業務影響がない
- 大量生成される(ログ系、中間テーブル)
対象例:
- 監査系:
operator_action_log,reservation_event,message_delivery_log - 中間テーブル(M:N):
customer_tag_link,menu_eligibility - 純内部マスタ:
business_hours,day_override,shift,cash_movement
bigserial列の運用ルール
- APIレスポンスのトップレベル項目として出さない(レビューでブロック)
- FK先がUUIDv7の場合、複合FKは
(store_id, parent_uuid)を使い、bigserial自身はFK先にしない - 中間テーブルは可能なら複合主キー
(a_id, b_id)で独立IDを持たせない最小構成を優先
7.2.3 表示用・共有用の別ID(必要に応じて追加)
Section titled “7.2.3 表示用・共有用の別ID(必要に応じて追加)”customer_no:店舗内で採番される顧客番号(会員証・受付票用、4〜6桁)。主キーとは別列coupon_code:人が入力・共有する短文字列(SUMMER2026等、または8〜10文字のランダム)salon_slug:公開URL用のスラッグ(hair-tokyo-shibuya等、グローバル一意)。店舗自体を一意に指す識別子のため、システム全体で UNIQUE。冪等な seed・公開URL設計の安定キーとして機能する
customer_no と coupon_code は主キーではなく、店舗内unique制約で管理する。salon_slug のみグローバルUNIQUE。
7.2.4 迷ったら
Section titled “7.2.4 迷ったら”UUIDv7側に倒す。「露出しないつもりだった列が後からAPIに露出する」事故は戻せないが、UUIDv7はoverkillでも害がない。
7.3 金額表現
Section titled “7.3 金額表現”- 整数
bigint(円単位) - 税抜
amount_excl_tax/ 税込amount_incl_tax/ 消費税額tax_amountを別列 - 税率は
tax_rate_pct(10 or 8)で保持 - 端数処理は四捨五入を標準、設定で切捨て/切上げ切替可
7.4 時刻
Section titled “7.4 時刻”- DB:
timestamptz(UTC保存) - アプリ:Luxon or Temporal(Temporal polyfill)
- 表示:
Asia/Tokyo - 予約の”時刻”は店舗タイムゾーン基準だが内部はUTCで処理
7.5 削除
Section titled “7.5 削除”- 物理削除はしない(
deleted_at列)、以下は例外- セッション・トークン類(物理削除OK)
- ジョブ(graphile-worker管轄)
- 一時ファイル
- 論理削除された行はRLSで見えなくなる設計にしない(集計に影響)、アプリ層でフィルタ
7.6 API契約
Section titled “7.6 API契約”- zod-openapiで契約ファースト:
packages/contractsにzodスキーマを定義、apps/apiがルート結線 - 型配布:
apps/apiがpackages/contracts/openapi.jsonを書き出し、scripts/codegen.sh(Docker経由)でopenapi-typescriptを実行、packages/contracts/src/generated/schema.tsを出力。FEはopenapi-fetch+paths型で参照 - 生成物はgitignore、
predev/prebuildフックで都度再生成 - パスプレフィクス
/api/admin/*— オペレーター認証必須/api/portal/*— 顧客(LINE)認証必須/mcp— MCP (OAuth2.1)/webhooks/*— 外部連携、HMAC検証
- レスポンス形式:成功は
{ data: ... }、エラーは{ error: { code, message, details? } } - ページング:
?limit=&cursor=(cursorベース、offset禁止)- 例外:店舗内で件数が小規模に閉じるマスタ(
staff/menu/equipment等、店舗あたり数十件程度)は、子SRSで明示した場合に限りmax 200件の全件返却を許容する。limitパラメータでクライアントが上限を絞ることはできるがcursorは要求しない。将来件数増大時に cursor 化する余地は残す
- 例外:店舗内で件数が小規模に閉じるマスタ(
- バージョニング:初期は不要、破壊的変更時にパス
/v2追加
7.7 認可
Section titled “7.7 認可”7.7.1 基本方針
Section titled “7.7.1 基本方針”- 全エンドポイントはデフォルト要認証
- 認可はミドルウェア + RLSの二重掛け
- オペレーター→店舗のスコープは
operator_store_link(operator_id, store_id, role_id)で管理
7.7.2 Permission モデル
Section titled “7.7.2 Permission モデル”permission-based + プリセット role の二層構造。
permission(全テナント共通マスタ、bigserial):key,description§7.1のstore_id必須制約の例外:システム共通マスタとして全店舗で同一、店舗が編集できない- キー命名は
{scope}:{resource}:{action}形式scope∈ {admin,portal,mcp}- 例:
admin:reservation:create,admin:reservation:override_business_hours,mcp:reservation:read
role(店舗スコープ、UUIDv7):store_id,key,name,is_preset- 店舗作成時にプリセット4種(owner/manager/staff/receptionist)を自動生成
role_permission(中間、bigserial)operator_permission_override(bigserial、Phase 1 で UI非公開):operator_id,store_id,permission_id,granted- 実効権限 = role の permission ∪ override(granted=true) − override(granted=false)
7.7.3 捨てやすさの設計原則
Section titled “7.7.3 捨てやすさの設計原則”permission-based を将来 RBAC に戻せるよう、以下の規律を守る。
packages/authに閉じ込める:permission チェックのロジックはこのパッケージのみ。外部からはrequirePermission(key)経由でのみ呼ぶ- permission キーは code-first な const:
packages/authがPERMISSIONS定数で export。seed は起動時にこの定数から DB へ UPSERT(マイグレーションにハードコードしない) - ドメイン層(
packages/domain)は permission を知らない:権限チェックをドメインロジックに混ぜない - override は feature flag で有効化:Phase 1 は flag=off、override テーブルを読まない分岐でリリース
- API 層は宣言的:
app.post(path, requirePermission(...), handler)のようにルート定義に貼る。handler 内部で個別チェックしない - テストバイパス窓口:
auth.bypassForTest()を用意、permission 粒度変更でテストが全壊しないようにする
7.7.4 Override の運用規律
Section titled “7.7.4 Override の運用規律”- override UI は単品リリース禁止:必ず「実効権限ビューア」(オペレーターごとの effective permission 一覧)とセットでリリース
- override は「プリセットから外れる例外」を意図し、恒久運用になったら該当 role を分岐させる(運用ガイドラインは Phase 3 で追記)
7.7.5 Permission 粒度変更のマイグレーション規約
Section titled “7.7.5 Permission 粒度変更のマイグレーション規約”permission の粒度は初期設計で必ず外れる。以下の手順を守る。
- split(1 permission を N に分割):新 permission 追加 → 旧 permission を持つ全 role/override に新 permission を付与するマイグレーション → 旧を deprecated(物理削除は 1 minor 後)
- merge(N permission を 1 に統合):新 permission を追加 → 旧のいずれかを持つ role/override に新を付与 → 旧を deprecated
- rename:
key変更は禁止。新 key を追加して旧を deprecated、seed で旧→新をマップ
7.7.6 RLS との境界
Section titled “7.7.6 RLS との境界”- 行ポリシーで解くべき制御(例:「自分担当の顧客だけ見える」)は permission で表現しない。RLS ポリシー(§7.1)側で解く
- permission で解くべき制御は「リソースに対する操作の可否」に限る(属性ベースに踏み込まない)
- ABAC(属性ベース認可)が必要になった時は、親SRS改訂を伴う判断とする
7.7.7 新 permission 追加時の preset role backfill 規約
Section titled “7.7.7 新 permission 追加時の preset role backfill 規約”新規permissionキーを追加する子SRSは、その migration の責務として、既存店舗のプリセット role に対する role_permission の backfill を必ず実施しなければならない。
理由:seed スクリプトは新規店舗の作成時にしか走らない。既存店舗に対しては migration が唯一の適用経路。これを忘れると「新店舗だけ permission を持つ/既存店舗は持たない」という不整合が永続化する。
手順:
- 新 permission キーを
packages/authのPERMISSIONS定数に追加 PRESET_ROLE_PERMISSIONSの対象ロールに新 permission を追記- migration で以下を実施:
-- 例: admin:reservation:cancel を manager と receptionist に付与INSERT INTO permission (key, description) VALUES (...) ON CONFLICT (key) DO UPDATE SET description = EXCLUDED.description;INSERT INTO role_permission (store_id, role_id, permission_id)SELECT r.store_id, r.id, p.idFROM role rCROSS JOIN permission pWHERE r.is_preset = trueAND r.key IN ('manager', 'receptionist')AND p.key = 'admin:reservation:cancel'ON CONFLICT DO NOTHING;
- テストで「全プリセット role が想定通りの permission 集合を持つ」ことを検証
§7.7.5 の split/merge/rename もこのbackfill規約に従う(実質同じ操作)。
7.8 監査
Section titled “7.8 監査”- 書込系は自動で
operator_action_logに記録 - 記録項目:
actor(operator_id or ‘system’)、action(‘reservation.create’等)、target、diff、ip、ua、at - 読取系の監査は明示的にsensitiveと指定したAPIのみ(顧客詳細閲覧等)
7.8.1 監査の例外
Section titled “7.8.1 監査の例外”以下は operator_action_log への記録対象外とする。CLI ログ・migration 履歴で十分に追跡可能なため:
- seed スクリプトの実行(
bun run db:seed、初期店舗作成・permission UPSERT 等) - bootstrap スクリプトの実行(
bun run scripts/db-bootstrap.ts、ロール作成・拡張・default privileges 設定) - DBマイグレーションの実行(
drizzle-kit migrate、スキーマ変更・preset role backfill 等)
これらは postgres superuser ないし migrator 接続で実行され、current_store_id を持たないため actor の確定が困難で、かつテナントスコープを超えた基盤操作である。operator_action_log テーブル自体が作成されるのは SRS-TEN-002 以降であり、それ以前の seed/bootstrap は監査対象とできない(テーブル不在)という実務的制約も反映している。
7.9 エラー処理
Section titled “7.9 エラー処理”- 予期エラー:ドメイン層で
DomainErrorを投げる、API層で4xxに変換 - 予期しないエラー:5xx + Sentry通知
- ユーザー向けメッセージは日本語・短く・次のアクションを示す
7.10 テスト
Section titled “7.10 テスト”- Unit:
packages/domainは100%に近い(純粋関数なので容易) - Integration:
apps/apiは主要ユースケース網羅(Testcontainers + 実Postgres) - E2E:Phase 2からPlaywright、クリティカルフロー(予約作成〜会計)のみ
- 契約テスト:zodスキーマをfixtureとしてFE/BE間で整合検証
7.11 セキュリティチェック
Section titled “7.11 セキュリティチェック”- Input validation は zod で全境界
- SQLインジェクションはDrizzleで防止(生SQLを書く場合はprepared statement必須)
- XSS:React標準のエスケープ + dangerouslySetInnerHTMLはレビュー必須
- レート制限:ログイン系 10回/分、一般API 100回/分 を Phase 1から
7.12 UI/UX
Section titled “7.12 UI/UX”- レスポンシブ:デスクトップ優先、タブレット(iPad)で動くこと必須、スマホはPWA経由
- アクセシビリティ:WAI-ARIA準拠、キーボード操作可能(業務利用で重要)
- 言語:日本語のみ
- ローディング:スケルトン表示、ブロッキングなグローバルローディング禁止
- エラー表示:Toastで通知 + フォームはインライン
- デザインシステム未整備:Phase 1〜2 の子SRS §4(UI仕様)は最低限の粒度に留める(画面の目的・主要要素の列挙・操作フロー・バリデーションまで)。ビジュアル・タイポグラフィ・コンポーネント選定には踏み込まない
- デザインシステム導入は別SRSとして起票(仮ID:SRS-UI-001、Phase 2〜3)。導入以降のUIは一括でシステムに置換し、既存の子SRS §4 はその時点で総改訂する前提
7.13 店舗設定(store_settings)
Section titled “7.13 店舗設定(store_settings)”store_settings(1 store = 1 行)で店舗ごとの設定を保持- Phase 1 に含む設定:
customer_required_fields=['name'](固定値、UI編集不可) - 動的フォーム(フィールド定義を設定で差し替える)は Phase 2 以降。設定項目がフォーム構造まで侵食する場合は DSL 化の判断を伴う
- 設定の参照は
packages/authや domain ではなく、API 層で読んで明示的に渡す
7.14 リソース適格性マスタ(メニュー対応)
Section titled “7.14 リソース適格性マスタ(メニュー対応)”メニュー × スタッフ × 設備の対応関係(「このメニューはどのスタッフが担当できるか」「このメニューにどの設備が必要か」)は単一テーブルでは表現しない。意味の異なる2軸を1表に同居させると NULL の多義(staff_id NULL の解釈/equipment_id NULL の解釈)が発生し、読みにくく壊れやすくなる。
7.14.1 スキーマ方針
Section titled “7.14.1 スキーマ方針”- スタッフ対応:
menu_staff_eligibility(store_id, menu_id, staff_id)— 行が存在 = そのスタッフが担当可 - 設備要件:
menu_equipment_requirement(store_id, menu_id, equipment_id)— 行が存在 = その設備が必要 - 両表とも複合主キーは
(store_id, menu_id, <staff_id|equipment_id>)、bigserialの独立IDは持たせない(§7.2.2 中間テーブル例外)
7.14.2 ホワイトリスト適用要否
Section titled “7.14.2 ホワイトリスト適用要否”メニュー側に ホワイトリスト適用要否のフラグ(仮: restrict_staff_required boolean、命名・型・デフォルトは SRS-MST-002 で確定)を持つ。
restrict_staff_required | 意味 | 予約作成時の挙動 |
|---|---|---|
false(既定) | 制限なし | 全スタッフが対応可。menu_staff_eligibility の行は無視 |
true | ホワイトリスト | menu_staff_eligibility の行に登録されたスタッフのみ対応可。0件のメニューは予約作成 API で MENU.NO_ELIGIBLE_STAFF を返してブロック |
「行0件 = 全員可」と「行0件 = 全員不可」をフラグで切り替える。既定は事故を起こしにくい側(制限なし)に倒す。制限が必要なメニューだけ明示的に opt-in する。
7.14.3 所有 SRS
Section titled “7.14.3 所有 SRS”menu_staff_eligibility/menu_equipment_requirementのスキーマ確定・CRUD・UI は SRS-MST-002(メニュー管理) が所有する- SRS-MST-001(スタッフ管理)は本節のテーブルを所有せず、
staffマスタのみを扱う
7.15 業務実体テーブルの複合参照標準
Section titled “7.15 業務実体テーブルの複合参照標準”§7.1 のマルチテナント規律で要求される「複合FK = (store_id, target_id) → target(store_id, id)」を Postgres の制約で実現するための標準形を固定する。
- 主キーは
id単独(UUIDv7、§7.2.1) - 追加で
UNIQUE(store_id, id)を必ず張る — 複合FKの参照先となるため Postgres は単独 PK 以外の UNIQUE 制約を要求する - 外部キーは複合FK
(store_id, foreign_id) REFERENCES other(store_id, id) - PK を
(store_id, id)複合にする案は採用しない(UUIDv7 単独参照の利便性を優先)
bigserial を採るログ系・中間テーブル(§7.2.2)は本節の対象外。中間テーブルは §7.14.1 のように複合主キーで独立IDを持たせない最小構成を優先する。
8. 技術スタック(確定)
Section titled “8. 技術スタック(確定)”要約のみ。詳細は DOC-DIR-001。
| 層 | 採用 |
|---|---|
| ランタイム | Bun 1.1+(Node互換モードで graphile-worker / Drizzle / better-auth 動作確認必須) |
| 言語 | TypeScript(strict) |
| APIフレームワーク | Hono |
| スキーマ/契約 | zod + @hono/zod-openapi |
| 型共有 | OpenAPI → openapi-typescript + openapi-fetch(Dockerでcodegen、pre-dev/pre-buildフック) |
| ORM | Drizzle ORM |
| DB | PostgreSQL 16 |
| 認証 | better-auth(Passkey、TOTP、OAuth server) |
| ジョブキュー | graphile-worker |
| リアルタイム | SSE(Server-Sent Events) |
| フロント | Vite + React + TanStack Router + Tailwind + shadcn/ui |
| 決済 | Stripe(国内はGMO-PG等も後で検討) |
| 通知 | WebPush(VAPID)/ Resend / LINE Messaging API |
| 監視 | Sentry、pino、Phase 3以降でOpenTelemetry |
| モノレポ | Bun workspaces(タスクランナー未採用) |
| パッケージマネージャ | Bun(bun install) |
| Lint | Biome |
| テスト | Vitest + Testcontainers + Playwright(Bun上で実行) |
| 本番Docker base | oven/bun:1-slim |
| 実行環境 | Docker Compose 必須(開発・本番とも)。ホスト直実行は禁止 |
9. フェーズ計画と子SRSカタログ
Section titled “9. フェーズ計画と子SRSカタログ”9.1 Phase 1:基幹(2〜3ヶ月)
Section titled “9.1 Phase 1:基幹(2〜3ヶ月)”| Child SRS ID | タイトル | BC |
|---|---|---|
| SRS-TEN-001 | 店舗作成・初期設定 | BC-TEN |
| SRS-TEN-002 | オペレーター登録・Passkey登録 | BC-TEN |
| SRS-TEN-003 | ロールと店舗アサイン | BC-TEN |
| SRS-MST-001 | スタッフ管理 | BC-MST |
| SRS-MST-002 | メニュー管理 | BC-MST |
| SRS-MST-003 | 営業時間・定休日・祝日 | BC-MST |
| SRS-MST-004 | シフト管理 | BC-MST |
| SRS-MST-005 | 設備管理 | BC-MST |
| SRS-CUS-001 | 顧客登録・検索 | BC-CUS |
| SRS-CUS-002 | 顧客詳細・メモ・タグ | BC-CUS |
| SRS-RES-001 | 予約カレンダー表示 | BC-RES |
| SRS-RES-002 | 予約作成 | BC-RES |
| SRS-RES-003 | 予約編集・移動 | BC-RES |
| SRS-RES-004 | 予約状態遷移(11状態) | BC-RES |
| SRS-RES-005 | 二重予約判定 | BC-RES |
| SRS-REG-001 | レジ会計(現金・カード) | BC-REG |
| SRS-REG-002 | 日次締め | BC-REG |
9.2 Phase 2:接客導線(2ヶ月)
Section titled “9.2 Phase 2:接客導線(2ヶ月)”| Child SRS ID | タイトル | BC |
|---|---|---|
| SRS-MSG-001 | 手動メッセージ送信(Email/LINE) | BC-MSG |
| SRS-MSG-002 | 予約リマインダー自動送信 | BC-MSG |
| SRS-MSG-003 | クーポン発行・適用 | BC-MSG |
| SRS-RES-006 | オンライン予約受付(顧客向けWeb) | BC-RES |
| SRS-PAY-001 | スマートペイ決済連携 | BC-PAY |
| SRS-PAY-002 | キャンセル料自動課金 | BC-PAY |
| SRS-RES-007 | 複数チャネル予約統合(電話/HPB/LIFF/直接) | BC-RES |
9.3 Phase 3:マーケ・分析・連携(2〜3ヶ月)
Section titled “9.3 Phase 3:マーケ・分析・連携(2〜3ヶ月)”| Child SRS ID | タイトル | BC |
|---|---|---|
| SRS-CUS-003 | 顧客セグメント・一斉配信 | BC-CUS, BC-MSG |
| SRS-ANL-001 | 売上日報・月次レポート | BC-REG |
| SRS-ANL-002 | 顧客分析(LTV、リピート率) | BC-CUS |
| SRS-MCP-001 | MCPサーバー(OAuth2.1) | cross |
| SRS-MCP-002 | MCPツールセット(予約・顧客・売上) | cross |
| SRS-PWA-001 | PWA化(Web Push、ホーム追加) | cross |
9.4 Phase 4:LIFF・メディア(2〜3ヶ月)
Section titled “9.4 Phase 4:LIFF・メディア(2〜3ヶ月)”| Child SRS ID | タイトル | BC |
|---|---|---|
| SRS-LIFF-001 | LIFF基盤(LINE Login、顧客紐付け) | BC-CUS |
| SRS-LIFF-002 | LIFF予約フロー | BC-RES |
| SRS-LIFF-003 | LIFF会員証・履歴 | BC-CUS |
| SRS-LST-001 | サロン基本情報掲載 | BC-LST |
| SRS-LST-002 | メニュー・クーポン掲載 | BC-LST |
| SRS-LST-003 | フォトギャラリー | BC-LST |
| SRS-LST-004 | ブログ・特集 | BC-LST |
| SRS-LST-005 | 口コミ返信 | BC-LST |
| SRS-LST-006 | スタッフ掲載情報 | BC-LST |
10. 子SRSのテンプレート
Section titled “10. 子SRSのテンプレート”各子SRSは以下の構造を最低限持つ。
# {Feature Name}
**Document ID**: SRS-{BC}-{NNN}**Parent**: SRS-ROOT-001 v0.X**Status**: Draft / Review / Approved / Implemented**Depends on**: [SRS-XXX-YYY, ...]
## 1. 目的## 2. ユーザーストーリー As a {role}, I want to {action}, so that {benefit}.## 3. ユースケース 3.1 主シナリオ 3.2 代替フロー 3.3 例外フロー## 4. UI仕様 4.1 画面構成(スクリーンショット or ワイヤーフレーム) 4.2 要素と挙動 4.3 バリデーション## 5. API仕様 5.1 エンドポイント一覧 5.2 zodスキーマ(リクエスト・レスポンス) 5.3 エラーコード## 6. データモデル影響 6.1 スキーマ変更(あれば) 6.2 マイグレーション計画## 7. 業務ルール - 状態遷移、制約、計算式## 8. 非機能要件(親SRS補足が必要な場合)## 9. セキュリティ・認可 - 必要ロール、RLS影響## 10. 受け入れ基準(Given-When-Then)## 11. テスト計画## 12. 関連ジョブ(graphile-worker)## 13. Open questions## 14. 変更履歴11. 実装フロー
Section titled “11. 実装フロー”1. 子SRS起票(Draft)2. レビュー(親SRS適合チェック、Open questions解消)3. Approved4. 実装 4.1 スキーマ変更あれば db:generate 4.2 contracts/ に zodスキーマ追加 4.3 domain/ にビジネスロジック 4.4 apps/api にルート追加 4.5 apps/web にUI追加 4.6 ジョブ追加なら jobs/ に定義5. テスト(unit + integration)6. PRレビュー7. マージ8. 子SRS → Implemented、変更履歴更新LLM連携時の手順:
- Context7で関連ライブラリの最新ドキュメントを事前取得
- 子SRSをプロンプトに貼り付ける
- 実装と同時にテストも出させる
- 人間は親SRS違反がないかをレビュー
12. Open Questions(親SRSレベル、未決)
Section titled “12. Open Questions(親SRSレベル、未決)”| # | 内容 | 締切の目安 |
|---|---|---|
| OQ-01 | 顧客データの 店舗間共有を許すか(チェーン運用ニーズ) | Phase 1後半 |
| OQ-02 | スタッフ指名料の取り扱い(メニュー加算 or 別テーブル) | SRS-MST-002作成時 |
| OQ-03 | スマートペイの決済事業者:Stripe / GMO-PG / LINE Pay 比較 | Phase 2開始時 |
| OQ-04 | HPB予約の取り込み方法:手動入力 / CSVインポート / スクレイピング禁止 | SRS-RES-007作成時 |
| OQ-05 | MCPツールのwrite系をどこまで解禁するか | SRS-MCP-002作成時 |
| OQ-06 | 本番デプロイ戦略:MiniPC継続 / VPS移行タイミング | Phase 2完了時 |
| OQ-07 | バックアップ戦略:pg_dump頻度・保存先・リストア手順 | Phase 1終了前 |
| OQ-08 | 消費税率変更への対応(歴史的税率保持) | SRS-REG-001作成時 |
| OQ-09 | Permission キーの Phase 1 初期リスト確定(何個で出荷するか、命名規約の当てはめ検証) | SRS-TEN-002 作成時 |
13. 変更履歴
Section titled “13. 変更履歴”| Version | Date | Author | Change |
|---|---|---|---|
| 0.1 | 2026-04-21 | yudai | 初版作成 |
| 0.2 | 2026-04-22 | yudai | 権限モデルを permission-based + プリセット role に拡充(§4.1, §7.7)。捨てやすさの設計原則・override 運用規律・粒度変更マイグレーション規約・RLS との境界を §7.7 に追記。店舗設定 store_settings を §7.13 に追加。子SRSのUI仕様を最低限粒度とし、デザインシステム導入SRS(仮 SRS-UI-001)で一括改訂する方針を §7.12 に追記。OQ-09 を追加 |
| 0.3 | 2026-04-25 | yudai | SRS-TEN-001 着手レビューと SRS-MST-001 起票準備の整合改訂を統合。§7.2.3: salon_slug を「グローバル一意」に修正(公開URL衝突回避と冪等seedキーの両立)。§7.6: ページング規則に小規模マスタ(staff / menu / equipment 等)の全件返却例外を追記。§7.7.7: 新 permission 追加時の preset role backfill migration 規約を新設(既存店舗への適用漏れ防止)。§7.8.1: seed/bootstrap/migration を監査記録の例外として明示。§7.14: 「リソース適格性マスタ」新設(menu_eligibility を menu_staff_eligibility / menu_equipment_requirement に分離、メニュー側ホワイトリスト適用要否フラグで「行0件=全員可/全員不可」を切替、既定は制限なし)。§7.15: 「業務実体テーブルの複合参照標準」新設(PK単独+UNIQUE(store_id, id)+複合FK の標準形を固定)。§9.4: Phase 4 カタログに SRS-LST-006 スタッフ掲載情報 を追加。ERD(DOC-ERD-001)の §1 BC-TEN を operator_store_link 前提に整合、§2 BC-MST を menu_eligibility 分離に整合 |
Appendix A. 子SRS作成時のチェックリスト
Section titled “Appendix A. 子SRS作成時のチェックリスト”起票時にコピペで使う。
- Document IDを採番(SRS-{BC}-{NNN})
- 親SRSのバージョンを明記
- 依存する他の子SRSを列挙
- ユーザーストーリーが1文で書けている
- 主シナリオ・代替フロー・例外フローが揃っている
- zodスキーマでリクエスト/レスポンス定義
- 親SRS §7(共通制約)に違反していない
-
store_id考慮済み(新規テーブルには必須) - RLS ENABLE + FORCE + POLICY をマイグレーションに含めた
-
appユーザーに必要な権限をGRANTした - リクエスト/ジョブで
SET LOCAL app.current_store_idが走る導線を確認 - ID方式判断:原則UUIDv7、bigserial許容なら§7.2.2の3条件すべて満たすか確認
- APIレスポンスにbigserial値が露出していない
- 表示用ID(customer_no等)が必要な場合は別列で設計
- 金額は整数・税抜/税込分離
- 時刻は timestamptz
- 論理削除(該当する場合)
- zod-openapi契約
- OpenAPI codegen の paths 型に反映(FEで確認)
- 監査ログ対象か判定
-
- 受け入れ基準がGiven-When-Thenで3件以上
- 関連ジョブ(graphile-worker)の有無を記載
- Open questionsは残っていない(残すなら親SRSに昇格検討)
Appendix B. 子SRSステータス管理
Section titled “Appendix B. 子SRSステータス管理”Draft 起票直後、レビュー前Review レビュー中Approved 実装可能、着手前In Progress 実装中Implemented マージ済み、受け入れ基準クリアDeprecated 廃止(後続SRSで置換)ステータス変更は子SRSファイル先頭の Status: 行で管理、gitログで追跡。