1ER図
コレクション間のリレーション(参照キーを中心に)
ID設計の方針: グループID は auto-id(F2)。いいねは auto-id(F3: ビジネスIDをdoc IDにせず将来の拡張に備える)。マッチは複合ID(重複防止)。サブコレクション(tables)は多店舗での番号衝突を回避(F1)。groups が席セッション管理も一元担当。
2コレクション一覧
全7コレクション。ルート / サブの分類
店舗マスタ。matchingPolicy・qrToken・status を保持。
テーブルマスタ。tableId・capacity・active・label。
ユーザープロフィール。Auth UID をドキュメントID に使用。グループ所属は groups.memberUids が一次情報。
グループ。auto-id。席セッション情報を統合。waiting → active → matched / expired の状態遷移(4段階)。
送信先グループのサブコレクション。受信いいねの取得がシンプル。doc ID = auto-id(likeId フィールドに同値を保持)。相互いいね → matches 生成。
マッチング成立レコード。ID は辞書順ソート済みグループID ペア。
管理者アカウント。role: shop | master。Custom Claims と同期。
3各コレクション詳細
全7コレクションのフィールド一覧(型・説明)
ID戦略: 手動採番(例: aoshiro-fujisawa) 用途: 店舗の基本情報・マッチングポリシー・QRトークンを管理
| フィールド | 型 | 説明 |
|---|---|---|
| shopId | string | 店舗ID(手動採番、例: "aoshiro-fujisawa") |
| name | string | 店舗名(例: "AOSHIRO Resort 藤沢店") |
| address | string | 住所 |
| lat | number | 緯度(位置情報管理が必要な場合) |
| lng | number | 経度(位置情報管理が必要な場合) |
| qrToken | string | 全席共通QRに埋め込む不変トークン(推測困難なランダム値) |
| status | enum | active / suspended / closed |
| matchingPolicy | object | マッチングポリシー設定 |
| └requireOppositeGender | boolean | 異性グループ間のみマッチング(既定: true) |
| └minMembersPerGroup | number | グループ最小人数(既定: 1) |
| └maxMembersPerGroup | number (null可) | グループ最大人数(既定: null = 上限なし) |
| └sessionTtlHours | number | セッション有効時間(既定: 6、G-3で確定) |
| createdAt | timestamp | 作成日時 |
| updatedAt | timestamp | 更新日時 |
ID戦略: テーブル番号を文字列IDとして使用(例: "1", "2") 用途: 各店舗のテーブル設定。多店舗間の番号衝突を避けるためサブコレクション採用(F1)
| フィールド | 型 | 説明 |
|---|---|---|
| tableId | string | テーブルID(doc IDと同じ値、汎用参照用) |
| tableNumber | string | テーブル番号(ユーザー手入力値と一致、例: "1", "2") |
| capacity | number | 定員人数 |
| active | boolean | テーブルの有効/無効フラグ |
| label | string | 表示名(例: "テーブル1番") |
| createdAt | timestamp | 作成日時 |
| updatedAt | timestamp | 更新日時 |
F1: 多店舗間でテーブル番号が重複するためサブコレクション採用。
ID戦略: Firebase Auth UID(Anonymous)をドキュメントIDとして使用(Auth と 1:1) 用途: 不変のユーザープロフィール情報を保持(5項目: name/age/gender/photoURL + uid/createdAt/updatedAt) ライフサイクル: 登録 Submit 時に作成。プロフィール更新時に updatedAt 更新。所属やステータス管理は groups コレクションで完結(groups.memberUids が単一の真実) v1差分: nickname/ageGroup/mood/wantToDo/message を廃止。さらに v2 で運用系(shopId/tableNumber/status/registeredAt/releasedAt/lastActiveAt)も廃止し、プロフィール情報+createdAt/updatedAt のみに簡素化
| フィールド | 型 | 説明 |
|---|---|---|
| uid | string | Firebase Auth UID(Anonymous Auth、doc IDと一致) |
| name | string | 名前 |
| age | number | 年齢(数値、G-2: 年齢層ではなく実年齢) |
| gender | enum | male / female(G-2: 2択) |
| photoURL | string | プロフィール写真のCloud Storage URL(G-6: JPEG/PNG/WebP、5MB、800x800にリサイズ) |
| createdAt | timestamp | 作成日時 |
| updatedAt | timestamp | 更新日時 |
ユーザーの所属グループは groups where memberUids array-contains uid でクエリする(groups.memberUids が単一の真実の源)。店舗別ユーザーカウントは users ではなく各グループの memberUids.length の合計で算出する(A4ダッシュボード。Firestore は array-length をクエリで直接使えないため、クライアント側で集計)。
ID戦略: Firestore auto-id(F2: 可読性よりコリジョン安全性を優先) ライフサイクル: 1人目登録で waiting 生成 → グループ確定で active → マッチ成立で matched。waiting/active のまま TTL 超過で expired(G-3)。案内完了は matches.status(guided)で管理。
| フィールド | 型 | 説明 |
|---|---|---|
| groupId | string | グループID(auto-id) |
| tableNumber | string | 入力されたテーブル番号 |
| status | enum | waiting / active / matched / expired |
| closedReason | enum (null可) | 終了理由: matched / manual / expired(未終了は null) |
| memberUids | array<string> | メンバーのUID配列(グループメンバーシップの単一の真実の源) |
| genderType | enum | male_only / female_only / mixed / unknown(F4: Cloud Functions で自動計算) |
| matchId | string (null可) | マッチ成立時のmatchID(未成立は null) |
| openedAt | timestamp | 1人目登録時(グループ生成日時) |
| readyAt | timestamp (null可) | グループ確定(active)日時 |
| matchedAt | timestamp (null可) | マッチ成立日時 |
G-3: TTL 超過で expired 化(TTL は shops.matchingPolicy.sessionTtlMinutes、既定: 60分)。G-4: マッチ成立直後の同テーブル新規登録は新規 group として生成。G-5: active 状態はメンバー追加可。matched 以降は不可。
ID戦略: doc ID = auto-id(Firestore自動採番)。likeIdフィールドにも同値を保持(F3) ライフサイクル: いいね操作で生成 → 相互いいね成立時に matchedAt を更新
| フィールド | 型 | 説明 |
|---|---|---|
| likeId | string | いいねID(doc IDと同じ値、Firestore自動採番) |
| fromGroupId | string | いいねを送ったグループID |
| fromUid | string | いいねを押したユーザーUID |
| createdAt | timestamp | いいねした日時 |
| matchedAt | timestamp (null可) | 相互いいね成立日時(未成立は null) |
F3: 送信先グループのサブコレクションとして配置。doc IDはauto-idで採番(ビジネスIDをdoc IDにせず、将来の再いいね等の拡張に備える)。G-7: いいね取り消しはPhase 1では不要。G-8: 受信いいね(片想い)は「あなたにいいねしたグループ」タブで表示。クエリは shops/{shopId}/groups/{自分のID}/likes を list。送信いいねは collectionGroup('likes').where('fromGroupId','==',自分のID)。マッチ成立判定はA→B作成時に逆方向 shops/{shopId}/groups/{A}/likes where fromGroupId == B を limit(1) でクエリ。
ID戦略: {groupAId}_{groupBId} の辞書順ソート済み複合ID(重複防止) ライフサイクル: 相互いいね成立で pending_guidance 生成 → 店員が案内開始で guiding → 案内完了で guided
| フィールド | 型 | 説明 |
|---|---|---|
| matchId | string | マッチID("{minGroupId}_{maxGroupId}" 辞書順ソート済み複合ID) |
| groupAId | string | グループAのID(辞書順で小さい方) |
| groupBId | string | グループBのID(辞書順で大きい方) |
| groupAUids | array<string> | グループAのメンバーUID配列(案内時の名簿用) |
| groupBUids | array<string> | グループBのメンバーUID配列(案内時の名簿用) |
| status | enum | pending_guidance(マッチ成立)/ guiding(店員着手中)/ guided(案内完了)/ cancelled |
| matchedAt | timestamp | マッチ成立日時(= 案内待ちキュー入場時刻) |
| guidingStartedAt | timestamp (null可) | 店員が案内を開始した日時 |
| guidedAt | timestamp (null可) | 案内完了日時 |
| cancelledAt | timestamp (null可) | キャンセル日時 |
A3 案内待ち画面クエリ: shops/{shopId}/matches where status in ['pending_guidance', 'guiding'] order by matchedAt asc。店舗横断統計は collectionGroup('matches') を使用。
ID戦略: Firebase Auth UID(Email/Password)をドキュメントIDとして使用 用途: 管理画面ログインユーザーの情報・ロール・担当店舗を管理
| フィールド | 型 | 説明 |
|---|---|---|
| uid | string | Firebase Auth UID(Email/Password) |
| string | メールアドレス | |
| displayName | string | 表示名 |
| role | enum | shop / master |
| shopIds | array<string> | 担当店舗IDの配列(shop ロール: 担当店舗のみ、master ロール: 空配列または ['*']) |
| active | boolean | アカウントの有効/無効フラグ |
| createdAt | timestamp | 作成日時 |
| lastLoginAt | timestamp (null可) | 最終ログイン日時(未ログインは null) |
G-10: Custom Claims 遅延対策として、重要操作は adminAccounts ドキュメント参照で二重チェック。
4設計概要
Firebase / Cloud Firestore — v2(多店舗対応・グループいいね方式)
v1(旧設計)との主な差分: 多店舗対応・グループ概念の導入・クーポン廃止・groups による席セッション管理の一元化(1コレクション化による重複削減)・genderType フィールド導入
| 項目 | 内容 |
|---|---|
| DB | Cloud Firestore (NoSQL) |
| 認証(来店客) | Firebase Anonymous Auth |
| 認証(管理者) | Email / Password + Custom Claims(role, shopIds) |
| 多店舗 | 初版から多店舗アーキテクチャ。shopId スコープ必須 |
| 登録項目 | 名前 / 年齢 / 性別(male|female)/ 写真 / テーブル番号 の5項目 |
| QR運用 | 全席共通QR。テーブル番号は手入力 |
| マッチング条件 | 男女グループ間のみ。相互いいねで成立 |
| 席解放 | マッチ成立 → matched(案内完了は matches.status: guided で管理) |
| クーポン | v2 スキーマ対象外(廃止) |
コレクション数とアーキテクチャ方針
全7コレクション。サブコレクション構造を採用:
- ルート(3):
users/adminAccounts/shops(Auth UID 1:1 または店舗マスタ) - shops 配下サブ(3):
tables/groups/matches(店舗スコープを構造で保証)。likesは groups のサブコレクションとして配置 groupsが席セッション管理・グループ管理・ライフタイム管理を一元担当- 店舗横断統計(A4ダッシュボード)は
collectionGroup('matches')等を使用 - ユーザーの所属グループは
groups where memberUids array-contains uidでクエリする(groups.memberUids が単一の真実の源)