データベース概要
使用 DB
PostgreSQL を使用する。ORM は Drizzle ORM で、heineken-survey-design-backend/src/db/schema.ts がテーブル定義の唯一の正となる。
テーブル一覧
設計データ
| テーブル名 | 論理名 | 説明 |
|---|---|---|
| users | ユーザー | システムのユーザー。認証時に自動作成される |
| surveys | 案件 | 案件(アンケート)。テーマ・ステータス・最新版番号を持つ |
| survey_permissions | 案件権限 | 案件ごとの共有権限(閲覧/編集) |
| survey_versions | 版 | 案件の版。保存操作のたびに追加される |
| survey_version_pointers | 版ポインタ | 案件の最新版を指すポインタ |
| survey_version_sections | セクション | 版に属するセクション(大問)のスナップショット |
| survey_version_questions | 設問 | セクションに属する設問のスナップショット |
| survey_version_question_options | 選択肢 | 選択肢・マトリクスの行列・FA の入力欄 |
| survey_version_question_branch_rules | 分岐ルール | 論理演算子・分岐先・メッセージ |
| survey_version_question_branch_conditions | 分岐条件 | 参照元設問・演算子・比較値 |
| survey_version_question_visibility_rules | 表示ルール | 表示ロジックのルール |
| survey_version_question_visibility_conditions | 表示条件 | 表示ロジックの条件(分岐条件と同一形状) |
| survey_version_question_visibility_targets | 非表示対象 | 表示ロジックで非表示にする選択肢 |
| question_library_items | 過去質問データ | 質問ライブラリ |
| user_survey_favorites | お気に入り | ユーザーごとの案件お気に入り |
外部インポート(Creative Survey)
| テーブル名 | 論理名 | 説明 |
|---|---|---|
| external_surveys | 外部調査票 | CS から取り込んだ調査票 |
| external_questions | 外部設問 | CS から取り込んだ設問 |
| external_answer_items | 外部選択肢 | CS から取り込んだ選択肢 |
| external_sub_items | 外部サブ項目 | CS から取り込んだサブ項目(マトリクスの列など) |
| external_logics | 外部分岐ロジック | CS から取り込んだ分岐ロジック |
| external_logic_items | 外部分岐条件 | CS から取り込んだ分岐条件 |
ER 図
設計データ
erDiagram
users ||--o{ surveys : "createdBy / updatedBy"
users ||--o{ survey_permissions : ""
users ||--o{ user_survey_favorites : ""
surveys ||--o{ survey_permissions : ""
surveys ||--o{ survey_versions : ""
surveys ||--|| survey_version_pointers : "latest"
surveys ||--o{ user_survey_favorites : ""
survey_versions ||--o{ survey_version_sections : ""
survey_version_sections ||--o{ survey_version_questions : ""
survey_version_questions ||--o{ survey_version_question_options : ""
survey_version_questions ||--o{ survey_version_question_branch_rules : ""
survey_version_questions ||--o{ survey_version_question_visibility_rules : ""
survey_version_question_branch_rules ||--o{ survey_version_question_branch_conditions : ""
survey_version_question_visibility_rules ||--o{ survey_version_question_visibility_conditions : ""
survey_version_question_visibility_rules ||--o{ survey_version_question_visibility_targets : ""
surveys ||--o{ question_library_items : "内部由来"
外部インポート
erDiagram
external_surveys ||--o{ external_questions : ""
external_questions ||--o{ external_answer_items : ""
external_questions ||--o{ external_sub_items : ""
external_questions ||--o{ external_logics : ""
external_logics ||--o{ external_logic_items : ""
external_surveys ||--o{ question_library_items : "外部由来"
external_questions ||--o| question_library_items : "外部由来"
共通ルール
| ルール | 内容 |
|---|---|
| 主キー | uuid(defaultRandom() で自動採番)。中間テーブルは複合主キー |
| 日時フィールド | timestamp with time zone。API では ISO 8601 文字列に変換して扱う |
| カラム名 | snake_case。TypeScript 側のプロパティは camelCase |
| 削除方式 | surveys のみ論理削除(deleted_at)。それ以外は物理削除 |
| 連鎖削除 | 親子関係のある外部キーには onDelete: cascade を設定する |
| 並び順 | sort_order(integer)で管理し、(親ID, sort_order) に一意インデックスを張る |
| enum | PostgreSQL の enum 型を使う。値の追加は必ず末尾に行う |
| 外部データの生 JSON | jsonb 型に保存する |
enum 一覧
| enum 名 | 値 |
|---|---|
survey_status |
下書き / レビュー中 / 出力済み / アーカイブ |
permission_role |
view / edit |
question_type |
single / multi / free_text / matrix / intro / pulldown |
branch_operator |
equals / includes / answered / not_includes / only / not_only / has_other |
branch_logical_operator |
AND / OR |
question_option_axis |
row / column |
enum への値追加は末尾のみ
中間への値挿入は drizzle-kit の ADD VALUE BEFORE 生成が不安定なため、新しい値は必ず末尾に追加する。question_type の pulldown が末尾にあるのはこのため。
設計方針
版のスナップショット構造
セクション・設問・選択肢・分岐は案件に直接ぶら下がるのではなく、版(survey_versions)に紐づくスナップショット として保存される。保存操作のたびに最新版の全内容が新しい版へ複製されるため、過去の版の内容は変更されない。
flowchart TD
S[surveys] --> V1[survey_versions v1]
S --> V2[survey_versions v2]
S --> V3[survey_versions v3<br/>最新版]
V3 --> Sec[survey_version_sections]
Sec --> Q[survey_version_questions]
Q --> Opt[survey_version_question_options]
Q --> BR[branch_rules]
Q --> VR[visibility_rules]
安定 ID
版が変わるとスナップショット行の id は新しく振られるが、stable_section_id / stable_question_id は複製時にそのまま引き継がれる。これにより、質問ライブラリが古い版のスナップショットを指していても、対応する設問を最新版から見つけられる。
分岐条件がラベル文字列を参照する理由
設問の更新では選択肢を毎回作り直すため、選択肢の id は安定しない。そのため分岐条件・表示ロジックの条件は選択肢の ID ではなく ラベル文字列 で対象を参照する。
分岐と表示ロジックの対称性
表示ロジックの条件テーブル(visibility_conditions)は、分岐の条件テーブル(branch_conditions)と 同じカラム構成 を持ち、branch_operator enum も共有する。違いは「分岐先を持つ」か「非表示にする選択肢のリストを持つ」かだけである。これは CS が logics と visibilities を対称な別系統 API として持っていることに合わせた設計。
マイグレーション
| コマンド | 内容 |
|---|---|
bun run db:generate |
schema.ts の変更から drizzle/ にマイグレーション SQL を生成する |
bun run db:migrate |
マイグレーションを DB に適用する |
bun run db:seed |
開発用の初期データを投入する |
bun run db:studio |
ブラウザで DB の中身を確認する GUI を起動する |
デプロイ環境では、drizzle/** の変更を契機に マイグレーション専用の Lambda が実行される。詳細は インフラストラクチャ を参照。