Concepts
データモデル (PostgreSQL スキーマ設計)
データモデル (PostgreSQL スキーマ設計)
定義
カケトクの PostgreSQL スキーマ全体像と、主要テーブル間の関係・外部キー・設計上の論点をまとめた概念ページ。要件定義書 §6.1 の DDL を起点に、運用観点の注意点を追加する。
現在の理解
テーブル関係図 (概要)
tenants (1) ─┬─< users
├─< sessions ─┬─< transcripts
│ ├─< escalations
│ ├─< structured_outputs
│ └─< usage_records
├─< campaigns ─< campaign_targets ──> sessions
├─< webhook_subscriptions ─< webhook_deliveries
├─< api_keys
├─< usage_aggregates
└─< audit_logs全テーブルに tenant_id が必須 (audit_logs は system_admin 操作のため NULL 可、users も system_admin は NULL 可)。RLS 適用対象。詳細は multi-tenant-rls。
テーブル別ポイント
tenants (顧客企業)
plan:variable | fixed | hybrid— 請求モデル (§3.9 と対応)settingsJSONB: 時間帯制限・発信 concurrency 上限 等、テナント固有設定
users (Web ログインアカウント)
tenant_idNULL 可 (system_admin 用)role:system_admin | tenant_admin | tenant_agent— 3 ロール ACLzoom_phone_number+zoom_phone_extension: エスカレーション時の<Dial>先status:offline | available | on_call— Dispatcher の候補選定に使用skills TEXT[]: Dispatcher のスキルベースルーティング (v1.1) で使用
sessions (通話セッション) — コアテーブル
twilio_call_sidUNIQUE: caller leg 不変 ID。転送後も不変 (call-session-continuity)twilio_conference_sid: Conference 方式 (Warm transfer) 使用時のみdirection:inbound | outboundcurrent_phase:ai | agent_a | agent_b | completed— 現在対応主体assigned_agent_id: 担当エージェント (tenant_agentロールの users.id)campaign_id: outbound 時のみ、inbound は NULL
transcripts (発話)
phase:ai | agent_a | agent_b— どの Phase で発話されたかspeaker:caller | ai | agent_a | agent_b— 話者track:inbound | outbound— Twilio Media Streams の track 由来 (物理話者分離の根拠)is_committed: partial → committed の 2 段階 (Scribe v2 Realtime の仕様)start_ms/end_ms: セッション開始からの相対タイムスタンプ
escalations (エスカレーション / 采配履歴)
from_actor→to_actorの遷移記録 (ai → agent_a 等)triggered_by:ai_auto | admin_manual | agent_request— 誰が発動したか (Dispatcher 監査要件)twiml_update_status:pending | applied | failed— TwiML 更新の実行状態- 参照: escalation-flow
structured_outputs (構造化結果)
schema_version: 将来スキーマ進化時の互換管理payloadJSONB: 案件種別・顧客要望・合意事項[]・次アクション[]・重要度 等 (§3.8 最小スキーマ)model: 使用した LLM モデル名 (Sonnet 4.6 / Opus 等) — 精度追跡に利用
campaigns / campaign_targets (アウトバウンド)
campaigns.script_configJSONB: AI 対話スクリプト設定 (紹介文・ヒアリング項目・エスカレーション条件)campaigns.allowed_hoursJSONB:[{"day": "mon", "from": "09:00", "to": "19:00"}, ...]campaign_targets.status:pending | in_progress | completed | failedcampaign_targets.attempts: 再架電ポリシーと連動 (FR-OUT-8)campaign_targets.session_id: 発信結果の sessions 参照 (多対一の可能性あり: 再架電で session が複数発生)
usage_records / usage_aggregates (利用状況)
usage_records.metric_type:call_duration_sec | twilio_cost_usd | scribe_seconds | tts_characters | claude_input_tokens | claude_output_tokens | media_streams_secondsusage_aggregatesは UNIQUE(tenant_id, period_type, period_start, metric_type) で冪等集計estimated_cost_jpy: 単価マスタ適用後の概算- 参照: usage-cost-tracking
webhook_subscriptions / webhook_deliveries
subscriptions.events TEXT[]:session.started,session.phase_changed,session.escalated,session.completed,session.structured_ready,campaign.completeddeliveries.status+attempts+next_retry_at: 指数バックオフ (最大 24 時間)response_code: HTTP ステータス保存で診断性を確保
api_keys
hashed_key: bcrypt/argon2 でハッシュ化 (NFR-SE-6)。生 Key は発行時のみ表示revoked_at: ソフト削除
audit_logs
tenant_idNULL 可 (system_admin 操作用)action: 正規化された操作名 (例:escalation.override,campaign.created)detailsJSONB: 操作固有情報ip_address/user_agent: 追跡性
RLS 設定 (§6.2)
全テーブルに以下のパターンでポリシー適用:
ALTER TABLE <t> ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON <t>
USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
CREATE POLICY system_admin_all ON <t>
USING (current_setting('app.current_role') = 'system_admin');Drizzle ORM の middleware で SET app.current_tenant_id を自動実行する。
論点
- transcripts の書込頻度: partial transcript をそのまま INSERT するか、committed のみに絞って partial は WS 中継のみにするか。前者は DB 書込負荷、後者は復旧時の欠損リスク
- structured_outputs のスキーマ進化:
schema_versionで管理するが、過去通話の再構造化ポリシーは未定 - campaign_targets → sessions の多重: 再架電で 1 target に複数 session が紐づく想定か、最新だけか。現状
session_idは単一カラムのため最新しか保持できない → 履歴保持が必要なら中間テーブル - usage_records の粒度: 通話ごと + metric_type ごとに 1 行だが、Claude の対話/構造化別に記録する場合は行数が膨らむ。パーティション設計が必要
- escalations の twiml_update_status: 非同期 (TwiML 更新は Twilio REST コール) なので、
applied遷移は Twilio からの callback で行う想定の実装詳細が未定 - audit_logs のリテンション:
tenants.settingsのデータ保存期間 (デフォルト 90 日、最大 1 年、NFR-CM-3) と整合を取る必要 - webhook_deliveries の DB 肥大: 成功分のアーカイブ戦略未定。リテンションか cold storage 移行か
インデックス候補 (想像、要件には明記なし)
sessions (tenant_id, started_at DESC): テナント別最新セッションsessions (twilio_call_sid): UNIQUE のため自動transcripts (session_id, start_ms): セッション内時系列取得escalations (session_id, created_at): セッション内采配履歴usage_records (tenant_id, occurred_at): 集計ジョブ用campaign_targets (campaign_id, status, next_retry_at): 発信ジョブのピックアップwebhook_deliveries (status, next_retry_at): リトライジョブのピックアップ
根拠となる資料
関連する概念
関連する entity
- 該当なし (現時点)
未解決の問い
- partial transcript の永続化方針 (書込頻度と復旧性のトレードオフ)
- campaign_targets ↔ sessions の多重性対応 (再架電履歴をどう持つか)
- usage_records のパーティション戦略 (月次 RANGE か、テナント別 HASH か)
- 構造化結果の再計算・スキーマ進化ワークフロー
- 監査ログのリテンションと cold storage 設計
更新メモ
- 2026-04-19: v3.0 要件定義書から初版作成。DDL 全体像と運用観点の論点を整理