テーマ
Requirements: データモデル
概要
Miss World Japan 2026 投票サイトで扱うデータの 要件レベルの定義。エンティティの存在・属性・関係・整合性ルールを実装非依存で記述する。
本サイトのデータは保存先の責務によって 3 系統に分かれる。
| 配置の責務 | データ種別 | 採用理由 |
|---|---|---|
| コンテンツ管理基盤(外部 Headless CMS) | コンテンツ系: 候補者、候補者画像、票数パッケージ | 運営によるコンテンツ編集・公開がコア業務であり、専用基盤に委譲することで自前管理画面・自前認証・自前画像配信を排除する |
| 取引データストア(マネージド RDBMS) | 取引系: 投票、決済、購入明細 | 決済プロバイダとの一次情報源として整合性・トランザクション境界が必要 |
| デプロイ環境設定 | 投票期間(開始日時・終了日時) | サイト全体で唯一の値であり、運用上の変更頻度が極めて低い。テーブル + シングルトン制約を排除する |
具体的なベンダー選定(コンテンツ管理基盤・取引データストア・デプロイ環境設定の各候補)、フィールド型・スキーマ定義・ER 図・マイグレーションは
design.md、技術選定の根拠はsteering/tech.mdを参照。
Boundary Context
- In scope: 上記 3 系統に保存される全データの存在・属性・関係・整合性ルール、および 3 系統をまたぐ参照(例: 取引データストアの Purchase がコンテンツ管理基盤の Candidate を参照する)の整合性ルール。
- Out of scope: 一般ユーザー向けアカウント・認証・セッション。運営者向け認証(コンテンツ管理基盤のメンバー機能に委譲し、本サイトの責務外とする)。決済プロバイダ側で管理される決済の詳細状態(決済取引・課金・払戻の各タイムスタンプ等は決済プロバイダを一次情報源として参照する)。
- Adjacent expectations:
- コンテンツ管理基盤は本サイトに対して、公開済みコンテンツの読み取り API、画像配信、運営者ロール / フィールド権限の強制(例: 票数パッケージのイミュータブル制約)、メンバー認証機能を提供する。
- 決済プロバイダは本サイトに対して、決済取引の作成・状態遷移・完了通知を提供する。
- デプロイ環境設定は本サイトに対して、設定値の永続化と再デプロイによる更新手段を提供する。
エンティティ一覧
| エンティティ | 論理名 | 配置 | 概要 |
|---|---|---|---|
| Candidate | 候補者 | コンテンツ管理基盤 | 候補者の情報 |
| CandidateImage | 候補者画像 | コンテンツ管理基盤 | 候補者に紐づく画像。実体はコンテンツ管理基盤の画像フィールドとして保持され、配信網経由で提供される |
| CreditPackage | 票数パッケージ | コンテンツ管理基盤 | 有料投票で選択可能な票数と価格の単位 |
| Vote | 投票 | 取引データストア | 1 票単位の投票記録。投票者のニックネームを保持する(認証ユーザーには紐付かない) |
| Purchase | 決済記録 | 取引データストア | 有料投票時の決済結果。1 決済に複数の票数パッケージ(数量付き)を含められる |
| PurchaseItem | 購入明細 | 取引データストア | 1 つの Purchase に含まれる「票数パッケージ × 数量」明細 |
| VotingPeriod | 投票期間 | デプロイ環境設定 | 本サイトの投票受付期間(開始日時・終了日時)。エンティティではなく環境設定値として扱う |
一般ユーザーアカウント非保有の方針: 本サイトは一般ユーザーのログイン機能を提供しない。投票/購入時にユーザーがニックネームを任意入力し、それを Vote / Purchase に保持する。同一文字列のニックネームは応援者ランキングで同一支援者として集計されるが、本人確認は行わず、なりすましは本サイトの保証対象外とする(ニックネーム空間は事実上公開・共有)。
運営者アカウント非保有の方針: 本サイトは運営者のログイン機能を自前で提供しない。運営者向けの認証・権限・監査はコンテンツ管理基盤のメンバー機能および管理 UI に完全委譲する。
共通属性(取引データストア配置エンティティに対する標準)
取引データストアに保存されるエンティティ(Vote / Purchase / PurchaseItem)は、以下の属性を標準で保持する。各エンティティ詳細の「属性一覧」表では原則として再掲しない。
| 属性 | 説明 |
|---|---|
| 識別子 | レコードを一意に識別する値 |
| 作成日時 | レコードが新規作成された日時(タイムゾーン付き) |
| 更新日時 | レコードが最後に更新された日時(タイムゾーン付き) |
コンテンツ管理基盤配置エンティティ(Candidate / CandidateImage / CreditPackage)の識別子・作成日時・更新日時は、コンテンツ管理基盤が標準で発行・管理するメタ情報を一次情報源として用いる(本サイトの責務外、ただし参照は可能)。
整合性ルール
- The data layer shall すべての取引データストア配置エンティティに識別子・作成日時・更新日時を保持させる
- The data layer shall 識別子をエンティティ内で一意にする
- The data layer shall 作成日時・更新日時をタイムゾーン付きで保持する
- The data layer shall 作成日時をレコード作成時に自動設定し、以後変更しない
- The data layer shall 更新日時をレコード作成時に設定し、以後の更新時に自動更新する
- The data layer shall イミュータブルなエンティティ(Vote 等)であっても更新日時を標準で保持する
エンティティ詳細
Feature 1: Candidate(候補者)
Purpose: ファイナリストとして掲載される候補者の表示用情報を保持し、Home / 候補者詳細 / ランキング画面に提供する。本サイト側ではテキスト情報を保持せず、コンテンツ管理基盤のコンテンツとして外部に保持する。 Scope: コンテンツ管理基盤上の候補者コレクション。
属性一覧
| 属性 | 必須 | 説明 | 例 |
|---|---|---|---|
| 表示名 | ✓ | サイト上で表示される候補者の名前 | 「アリサ」 |
| 出身地 | ✓ | 候補者の出身地(都道府県等) | 「東京」 |
| 年齢 | ✓ | 候補者の年齢 | 23 |
| 身長 | ✓ | 候補者の身長(cm) | 168 |
| 職業 | ✓ | 候補者の職業 | 「モデル」 |
| 趣味・特技 | 趣味・特技の項目(0 件以上) | 「ヨガ」「カメラ」 | |
| 資格・大会実績 | 資格・大会実績の項目(0 件以上) | 「TOEIC900 点」 | |
| 座右の銘 | 候補者の座右の銘 | – | |
| 夢 | 候補者の夢・目標 | – | |
| PR メッセージ | 候補者からの長文メッセージ(改行を含む) | – | |
| 表示順 | ✓ | 候補者一覧で表示する順序を制御する値 | – |
| 候補者画像 | CandidateImage の 0 件以上のリスト | – |
Acceptance Criteria (EARS)
- The CMS schema shall 表示名を必須とし空文字を許容しない
- The CMS schema shall 趣味・特技、資格・大会実績をそれぞれ 0 件以上のリスト型として扱う
- The CMS schema shall PR メッセージで改行(複数の段落)を保持できる型を採用する
- The CMS schema shall 候補者に 0 件以上の候補者画像を関連付けられる
- When 候補者がコンテンツ管理基盤で「公開」状態に設定された, the public site shall 当該候補者を Home / ランキング / 詳細画面に表示する
- When 候補者がコンテンツ管理基盤で「下書き」または「停止」状態に設定された, the public site shall 当該候補者を非表示にする
- If 取引データストア側の Vote / Purchase が当該候補者を参照している, the CMS operation shall 当該候補者の物理削除を行わせない(運用ルールとして「停止」状態への変更で対応する)
Feature 2: CandidateImage(候補者画像)
Purpose: 候補者に紐づく画像を提供し、画像本体はコンテンツ管理基盤の配信網から提供する。本サイト側で画像バイナリを保持・配信せず、外部配信網経由で最適化して配信する。 Scope: コンテンツ管理基盤の候補者コンテンツが保持する画像フィールド(複数画像)。
属性一覧
| 属性 | 必須 | 説明 |
|---|---|---|
| 関連候補者 | ✓ | 候補者コンテンツへの帰属(コンテンツ管理基盤の親子関係) |
| 画像 URL | ✓ | コンテンツ管理基盤が発行する配信網上の画像 URL |
| 画像形式 | ✓ | 画像 MIME 型(JPEG / PNG / WebP 等) |
| 表示順 | ✓ | 候補者内での画像表示順序(先頭をメイン画像として扱う) |
Acceptance Criteria (EARS)
- The CMS schema shall 各候補者画像が関連候補者・画像 URL・画像形式を必須として持つ
- The CMS schema shall 画像形式を JPEG / PNG / WebP のいずれかに限定する
- The CMS schema shall 表示順を同一候補者内で重複させない(運用ルール)
- The public site shall 表示順が先頭の画像をメイン画像として一覧表示で使用する
- When 候補者がコンテンツ管理基盤から削除された, the CMS storage shall 関連する候補者画像も同時に消滅させる(基盤側の親子関係による連動)
- The CMS storage shall 候補者画像のサイズ上限(具体値は design.md で定義)を超えるアップロードを拒否する
- The public site shall 候補者画像を外部画像配信網経由で提供し、当アプリ側に画像バイナリを保持しない
Feature 3: CreditPackage(票数パッケージ)
Purpose: 有料投票で選択可能な「票数 × 価格」の単位を提供する。決済成功時にこの票数 × 購入数量分の Vote が即時生成され、残数として保留する仕組みは持たない。本サイト側ではマスタを保持せず、コンテンツ管理基盤上の票数パッケージコレクションを参照する。 Scope: コンテンツ管理基盤上の票数パッケージコレクション。
属性一覧
| 属性 | 必須 | 説明 |
|---|---|---|
| 名称 | ✓ | 表示用のパッケージ名 |
| 票数 | ✓ | パッケージ 1 つあたりの投票数 |
| 価格(税込) | ✓ | 円単位の販売価格 |
| 表示順 | ✓ | パッケージ一覧内での表示順序 |
| アクティブ状態 | ✓ | 販売中(true) / 廃止(false)。新規購入画面で表示されるのは true のみ |
Acceptance Criteria (EARS)
- The CMS schema shall 票数を 1 以上の整数として保持する
- The CMS schema shall 価格を 0 以上の整数(円)として保持する
- The CMS role/field permission shall 作成済み票数パッケージの名称・票数・価格・表示順を運営者であっても更新できないように強制する(完全イミュータブル)
- The CMS role/field permission shall 票数パッケージの物理削除を運営者であっても実行できないように強制する
- The CMS role/field permission shall アクティブ状態のフィールドのみ運営者が更新できるように許可する
- The public site shall 新規購入画面でアクティブ状態が true のパッケージのみを提示する
- When 価格・名称・票数を変更したい運営要件が発生した, the CMS operation shall 新規 CreditPackage を追加し旧レコードの「アクティブ状態」を false に変更することで対応させる
Feature 4: Vote(投票)
Purpose: 候補者へ投じられた 1 票の記録を保持し、得票数・応援者ランキングの集計元として提供する。本サイトの投票は有料のみのため、Vote は必ず Purchase に紐づき、Vote 自身はイミュータブルとして扱う。 Scope: 取引データストア上の投票エンティティ。
属性一覧
| 属性 | 必須 | 説明 |
|---|---|---|
| 対象候補者 | ✓ | 候補者コンテンツの識別子への参照(取引データストア → コンテンツ管理基盤の越境参照、外部キー制約は持たない) |
| 由来する購入 | ✓ | Purchase への参照 |
| ニックネーム | ✓ | 投票者が購入時に入力した任意文字列。応援者ランキング集計のキーとなる |
Acceptance Criteria (EARS)
- The data layer shall 投票の対象候補者・由来する購入・ニックネームを必須とする
- The data layer shall ニックネームを 1 文字以上、運用上定める最大長以内(design.md で定義)とし、前後の空白をトリムする
- The data layer shall Vote が必ず由来する Purchase を持つようにする(無料投票経路を作らない)
- The data layer shall 投票記録を事後変更不可(イミュータブル)として扱い、有効/無効の状態を Vote 自身ではなく由来する Purchase の決済ステータスから導出する
- The aggregation service shall 候補者に紐づく総得票数を、由来する Purchase の決済ステータスが「成功」である Vote のみを対象に集計する
- The aggregation service shall 候補者ごとの応援者ランキングを、上記の集計対象 Vote をニックネーム単位でグループ化し票数の多い順に導出する
- When 関連する Purchase が払戻された, the aggregation service shall その購入由来の Vote を集計対象から外す(Vote レコード自体は変更しない)
- If 対象候補者のコンテンツ管理基盤上のコンテンツが「停止」または削除済みである, the public site shall 当該候補者を一覧から除外するが Vote レコード自体は監査証跡として保持する
ニックネームによる集計の取り扱い: ニックネームは文字列マッチで集計するため、表記揺れ(大文字小文字、全角半角、前後空白)は事前正規化が必要。具体的な正規化規則は design.md で定義する。
Feature 5: Purchase(決済記録)
Purpose: 有料投票時の決済結果を保持し、決済プロバイダとの一次連携状態を表現する。1 つの Purchase は複数の票数パッケージを数量付きで組み合わせた単一決済を表し、決済成功時に同時生成される Vote 群と紐づく。 Scope: 取引データストア上の決済記録エンティティ。
属性一覧
| 属性 | 必須 | 説明 |
|---|---|---|
| 対象候補者 | ✓ | 候補者コンテンツの識別子への参照(越境参照、外部キー制約は持たない) |
| ニックネーム | ✓ | 投票者が入力したニックネーム(Vote にも複製される) |
| 決済プロバイダの取引識別子 | ✓ | 決済プロバイダが発行する一意の取引識別子 |
| 決済ステータス | ✓ | 処理中 / 成功 / 失敗 / 払戻 |
日時情報の取り扱い: 決済の開始日時は共通属性の作成日時で代用する。決済成立日時・払戻日時等の状態遷移時刻は決済プロバイダ側(決済取引・課金・払戻の各タイムスタンプ)を一次情報源とし、Purchase 行には独立して保持しない。最新の状態遷移時刻は共通属性の更新日時で参照可能。
合計金額・合計票数の取り扱い: 関連する PurchaseItem 群から導出する。Purchase 行には保持しない(短期開催のため集計コストは無視できる、正規化を優先)。決済プロバイダへ渡す請求金額は決済開始時に PurchaseItem から計算する。
Acceptance Criteria (EARS)
- The data layer shall 同一の決済プロバイダの取引識別子を持つ決済レコードを存在させない(重複決済防止)
- The data layer shall Purchase が 1 件以上の PurchaseItem を必ず持つようにする(空の購入を不可とする)
- When 決済が成功した, the purchase service shall 関連 PurchaseItem の「関連パッケージの票数 × 数量」の合計分の Vote レコードを即時生成し、各 Vote にその Purchase への参照と Purchase 側のニックネームを継承させる
- When 決済が失敗した, the purchase service shall 関連する Vote レコードを生成しない
- When 払戻が発生した, the purchase service shall 決済ステータスを「払戻」に更新し、紐づく Vote レコード自体は削除も変更もしない(集計側で Purchase の決済ステータスにより除外する)
- The data layer shall Purchase.対象候補者 がコンテンツ管理基盤側で削除された場合でも、取引データストア上の Purchase レコードを孤立させずに保持する(越境参照のため整合性は最終的に集計時点で吸収する)
Feature 6: PurchaseItem(購入明細)
Purpose: 1 つの Purchase に含まれる「票数パッケージ × 数量」の明細を表す。CreditPackage がコンテンツ管理基盤側で完全イミュータブルなため、購入時点の値はコンテンツ識別子から常に参照解決でき、スナップショットは保持しない。 Scope: 取引データストア上の購入明細エンティティ。
属性一覧
| 属性 | 必須 | 説明 |
|---|---|---|
| 関連購入 | ✓ | Purchase への参照 |
| 関連パッケージ | ✓ | 票数パッケージコンテンツの識別子への参照(越境参照、外部キー制約は持たない)。票数パッケージは完全イミュータブルかつ削除不可のため常に解決可能 |
| 数量 | ✓ | 同じパッケージを何個購入したか(1 以上の整数) |
名称・票数・価格・小計の取り扱い: CreditPackage が完全イミュータブル(削除・編集不可)であるため、これらは関連パッケージから常に正しい値が取得可能。スナップショットを PurchaseItem 側に持たず、参照解決で導出する。
Acceptance Criteria (EARS)
- The data layer shall 関連購入・関連パッケージ・数量を必須とする
- The data layer shall 数量を 1 以上の整数とする
- The data layer shall 同一 Purchase 内で同じ CreditPackage を複数明細として持たせず、必ず 1 明細にまとめて数量で表現する(運用ルール)
- When Purchase が削除された, the data layer shall 関連する PurchaseItem も連鎖削除する
- The data layer shall PurchaseItem を事後変更不可として扱う(価格改定が必要な場合は新規 CreditPackage + 新規 Purchase で対応する)
Feature 7: VotingPeriod(投票期間)
Purpose: 本サイトが対象とする投票受付期間の値を提供する。本サイトは単一の開催のみを対象とし、投票期間はサイト全体で唯一の値である。 Scope: デプロイ環境設定上の投票期間値(開始日時・終了日時)の データ定義 のみ。期間内/外の業務ルール(受付可否)は steering/product.md「投票期間」が定義する。
属性一覧
| 属性 | 必須 | 説明 | 例 |
|---|---|---|---|
| 開始日時 | ✓ | デプロイ環境設定として保持する投票受付開始日時(タイムゾーン付き ISO 8601) | 2026-04-01T00:00:00+09:00 |
| 終了日時 | ✓ | デプロイ環境設定として保持する投票受付終了日時(タイムゾーン付き ISO 8601) | 2026-04-30T23:59:59+09:00 |
Acceptance Criteria (EARS)
- The deployment platform shall 開始日時・終了日時をタイムゾーン付き ISO 8601 文字列として保持させる
- The voting period service shall デプロイ環境設定からパースした終了日時が開始日時より後であることを検証する
- The voting period service shall サイト内で投票期間を必要とする全画面に対し、デプロイ環境設定から導出された唯一の値を提供する
- If 設定値が未設定または不正な書式である, the voting period service shall アプリケーション起動時または初回参照時にエラーとして扱う(エラー時の画面挙動・受付拒否は
steering/product.mdを参照) - When 開催期間を変更したい運用要件が発生した, the deployment process shall デプロイ環境設定を更新し再デプロイすることで反映させる(2 件目以降のレコード追加経路は存在しない)
期間内/外の業務ルール(受付可否・サーバー側ガード・UI 抑止)は本 spec では定義しない。プロダクト全体を横断する不変条件として
steering/product.md「投票期間」で定義する。
エンティティ間の関係
コンテンツ管理基盤 取引データストア
────────────── ──────────────
Candidate ───────────────────(参照)── Vote
│ ▲ ▲
│ │ │
│ └────────────────────(参照)──── Purchase >── PurchaseItem ──(参照)── CreditPackage
│ ▲
└──< CandidateImage(基盤側の親子関係) │
CreditPackage ⊂ コンテンツ管理基盤関係の詳細
| 関係 | カーディナリティ | 配置の越境 | 削除時の挙動 |
|---|---|---|---|
| Candidate : Vote | 1 : 0..* | あり | Candidate の物理削除は運用上禁止(コンテンツ管理基盤側で「停止」状態に変更する) |
| Candidate : CandidateImage | 1 : 0..* | なし | Candidate 削除時にコンテンツ管理基盤の親子関係により連動消滅 |
| Candidate : Purchase | 1 : 0..* | あり | Candidate の物理削除は運用上禁止 |
| Purchase : Vote | 1 : 1..* | なし | Purchase 払戻時も Vote レコードは保持(集計時に除外) |
| Purchase : PurchaseItem | 1 : 1..* | なし | Purchase 削除時に PurchaseItem は連鎖削除 |
| CreditPackage : PurchaseItem | 1 : 0..* | あり | CreditPackage はコンテンツ管理基盤のロール/フィールド権限で削除不可(完全イミュータブル) |
越境参照の整合性: 取引データストア側のレコード(Vote / Purchase / PurchaseItem)はコンテンツ管理基盤のコンテンツ識別子を文字列として保持し、データストアレベルの外部キー制約は持たない。整合性は (1) コンテンツ管理基盤側で物理削除を運用上禁止、(2) 集計時にコンテンツ管理基盤から取得した公開済みコンテンツとの照合で吸収する。
集計・導出されるデータ
実テーブルとしては保持せず、必要時に集計で導出する値:
| 導出データ | 集計元 | 用途 |
|---|---|---|
| 候補者の現在得票数 | 取引データストアの Vote を対象候補者でカウント。由来 Purchase の決済ステータスが「成功」のものに限る | home / ranking で表示 |
| 候補者の現在順位 | 全候補者の得票数を降順ソート | 全画面 |
| 候補者ごとの応援者ランキング | 上記の集計対象 Vote を対象候補者 × ニックネームでグループ化、票数降順 | candidate-detail |
キャッシュテーブルは作らない: 得票数の事前集計テーブル(キャッシュテーブル)は、状態を増やし整合性課題を持ち込むため採用しない。集計はページ生成時に取引データストアへ都度クエリを投げて行う(短期開催のため負荷は無視できる)。
ライフサイクルとイベント
各エンティティの主要なライフサイクル:
Candidate
- 運営がコンテンツ管理基盤の管理画面で候補者コンテンツを登録(下書き状態)
- 関連する画像を同コンテンツの画像フィールドにアップロード(コンテンツ管理基盤の配信網に格納される)
- 公開状態に変更 → 投票期間中は本サイトに表示される
- 投票期間終了後も Vote / Purchase との越境参照保持のためコンテンツは残す(必要に応じて「停止」状態にする)
CandidateImage
- 運営がコンテンツ管理基盤の候補者コンテンツに画像をアップロードすると、基盤側が配信網に画像本体を保存し画像 URL を発行する
- 本サイトが基盤の読み取り API 経由で画像 URL を取得し、配信網経由でユーザーに提供する
- 候補者コンテンツ削除時に画像も連動消滅する(コンテンツ管理基盤の親子関係)
CreditPackage
- 運営がコンテンツ管理基盤の管理画面で票数パッケージコンテンツを登録(名称・票数・価格・表示順を確定)
- 完全イミュータブル(コンテンツ管理基盤のロール/フィールド権限で名称・票数・価格・表示順の編集・削除を不可にする)
- 価格・名称・票数を変更したい場合は新規票数パッケージコンテンツを追加し、旧レコードの「アクティブ状態」を false に変更する
- 新規購入画面では本サイトがコンテンツ管理基盤から取得したアクティブ状態 true のコンテンツのみを提示する
Vote
- Purchase 成功時、関連 PurchaseItem の「関連パッケージの票数 × 数量」の合計分の Vote が取引データストアに一括生成される(無料投票経路は存在しない)
- ニックネームは由来する Purchase からコピーされる
- 完全イミュータブル(作成後は変更も削除もされない)。有効/無効の判定は由来する Purchase の決済ステータスから導出する
Purchase
- ユーザーが購入画面で複数パッケージを数量付きで組み合わせ、決済確定操作
- 決済プロバイダからの完了通知受信で「処理中 → 成功 / 失敗」に遷移
- 成功時、関連 PurchaseItem から算出した合計票数分の Vote レコードが一括生成される
- 払戻発生時は「払戻」に遷移する(紐づく Vote レコード自体は変更しないが、集計から除外される)
PurchaseItem
- Purchase 作成時に同時生成される(同じトランザクション内)
- 関連パッケージ・数量は事後変更不可(価格・名称・票数は CreditPackage の不変性により保証される)
VotingPeriod
- 運用開始時にデプロイ環境設定として開始日時・終了日時をセットし、デプロイする
- 現在日時が開始日時〜終了日時の範囲内であれば「投票受付中」として扱う
- 期間の修正が必要な場合は、運営がデプロイ環境設定を更新して再デプロイする(2 件目以降の追加経路は存在しない)
投票無効化の扱い
Vote は完全イミュータブルとし、無効化のための独立した状態を Vote 自身には持たない。払戻による無効化は 由来する Purchase の決済ステータス(払戻)から導出する。
- 集計時は Vote を Purchase と結合し、Purchase の決済ステータスが「成功」の Vote のみを対象とする
- Vote レコード自体は払戻後も削除・変更せず、監査証跡として残す
- 無効化の事実は Purchase 側のステータス遷移履歴(決済プロバイダ側の管理画面ログを含む)から確認できる
非機能要件
- 個人情報の最小化: 一般ユーザーから収集するのはニックネームのみ。氏名・住所・メールアドレスを直接保持しない(決済プロバイダのワンクリック決済利用時のメール等は決済プロバイダ側で管理)
- トランザクション境界: 「購入成功 → PurchaseItem から算出した合計票数分の Vote 一括生成」は取引データストア上の同一トランザクション内で実施する
- 整合性: Vote の有効性は由来する Purchase の決済ステータスから一意に導出される(Vote 側に独立した状態を持たない)
- 越境参照の整合性: 取引データストア → コンテンツ管理基盤の越境参照(候補者・票数パッケージへの参照)は外部キー制約を持たないため、コンテンツ管理基盤側の物理削除を運用上禁止することと、集計時にコンテンツ管理基盤から取得した公開済みコンテンツとの照合で吸収する
- 画像配信: 候補者画像はコンテンツ管理基盤の配信網がオリジン、本アプリ側は画像バイナリを保持・再配信せず最適化済み配信を仲介するのみとする
- コンテンツ取得のキャッシュ: 公開サイトからコンテンツ管理基盤への問い合わせは短 TTL(60〜180 秒程度)のキャッシュに乗せて許容する。コンテンツ更新を即時反映するプッシュ通知連動は採用しない(通知の配送失敗による永続不整合状態を避けるため)
- コンテンツ取得用 API キー: 公開サイトから利用する API キーは読み取り専用権限のものに限定する
セキュリティ・冪等性・監査ログ等のサイト共通要件は
steering/tech.mdの「セキュリティポリシー」を参照。
依存仕様
本 spec は基盤層であり、他 spec に依存しない。本 spec が定義するエンティティは home / candidate-detail / candidate-ranking / voting から参照される(依存方向は他 spec → 本 spec の一方向)。
運営者向けコンテンツ管理はコンテンツ管理基盤に完全委譲する方針のため、
adminspec は持たない。コンテンツ管理基盤上のコレクションスキーマ・メンバー権限・公開状態管理の要件は本ドキュメントに統合されている。本サイト側に自前管理画面・運営者認証・書き込み API を実装しない方針はsteering/structure.mdおよびsteering/product.mdで固定する。