ガイド 第11

コアデータモデル

Session/Speaker/Stage/Venue/Sponsor

@event/schema

@event/schema は xdeal-lp / spikes-lp / ivs-cms の3実装を照合した単一のドメイン型セット。 最リッチな ivs-cms src/types.ts を起源に採取し、3つの規約に正規化した:

  1. ID は全域 id — spikes の _id、xdeal のレガシー SessionItem / VenueRoom を退役
  2. LOCKED フィールドは 4S が所有し、CMS は非破壊オーバーレイのみ書く
  3. イベント固有フィールドは型フォークで足さない — 拡張は extra-fields スロット(DB extension 層・第15章)へ

型の全体像

型はすべて @event/schema から直接 import する(internal-package なのでビルド成果物は無い):

import type {
  Session, Speaker, Room, Venue, FloorPlan, Sponsor, PassTier,
  Visibility, Track,
} from "@event/schema";
import { LOCKED_SESSION_FIELDS, CARD_OVERLAY_FIELDS } from "@event/schema";
役割id備考
Sessionセッション(登壇枠)id = 4S session idLOCKED + CMS overlay + card overlay の3層
Speaker登壇者idfourSUserId / fourSSlug で 4S user に連結
Roomステージ / 部屋idSession.roomId の参照先。code("M"/"UP")は timetable chip 用
Venue会場idrooms: Room[] を内包。未マッピング stage は合成 venue に集約
FloorPlanフロア図idsvgUrl / svgUrlEn / imageUrl
Sponsorスポンサーidtier は自由文字列(ivs PARTNERS の8段が superset)
PassTier参加券種idprice / paymentType / salesLimit

> 「Stage」は独立した型ではなく Room が担う。Session.roomIdRoom.id で解決し、 > どの Venue にも属さない stage は Venue.rooms の合成エントリに畳み込まれる。

LOCKED(4S 所有)と CMS overlay の境界

Session は3層で構成される。LOCKED は 4S が正で、CMS からは書き込まない:

export const LOCKED_SESSION_FIELDS = [
  "id", "title", "titleEn", "startsAt", "durationMin", "roomId", "speakerIds",
] as const satisfies readonly (keyof Session)[];
フィールド誰が書くか
LOCKEDid title titleEn startsAt durationMin roomId speakerIds4S のみ — CMS の merge 層はこの集合への書き込みを拒否
CMS overlaytrack featured genreTags visibility description* coverImageUrl languageadmin(content/*.json)
card overlaycardVariant cardTitle cardSpeakerIds cardScale@event/session-card が所有

speakerIds は 4S sessionSpeakers[].order 順で固定。moderatorId4S role から毎回導出し保存しない (型上も「保存禁止」と注記される)。title を表示側で改行したいときは LOCKED を書き換えず、 overlay 側の cardTitle\n で明示改行)で上書きする。

card overlay のドリフト防止

手動微調整オーバーレイのフィールドは CARD_OVERLAY_FIELDS に登録され、content 読み込みと KV ミラーが 同じ集合を参照する(ivs-cms の metaOverlay.ts / pickSessionOverlay パターン)。片方だけ増える ドリフトを防ぐための単一リストである:

export const CARD_OVERLAY_FIELDS = [
  "cardVariant", "cardTheme", "cardTitle", "cardTitleEn", "cardSpeakerIds",
  "cardRoles", "cardRolesEn", "cardScale", "cardTitleScale", "cardRoleScale",
  "cardNameScales", "cardSpeakerIdsUpdatedAt",
] as const satisfies readonly (keyof Session)[];

cardSpeakerIdsUpdatedAt は 4S 側 lineup 編集との競合を last-writer-wins で解決するためのタイムスタンプ。

可視性と多言語

  • Visibility = "public" | "private" | "preview" | "draft"draft は admin のみ、preview

previewToken 保持者のみに見える。公開ページは s.visibility === "public" で絞る。

  • Trackstring エイリアス — トラック語彙はイベント側に委ねる(enum 固定しない)。
  • 多言語は型フォークせず併置フィールドで持つ: title/titleEndescription/descriptionEn

nameJa/nameEnSession.language"ja" | "en" | "bilingual"

Speaker / Sponsor の要点

SpeakerrevealStatus"anonymous" | "revealed")で伏せ登壇に対応し、fourSUserId / fourSSlug(可変)で 4S 公開プロフィールへ連結する。Sponsor.tier / Room.code のように 語彙をイベント側に開いた文字列は型で縛らない — superset を持つ ivs を基準に、他イベントは部分集合で使う。 これが「型を1つに保ったまま各イベントが違う語彙で運用する」ための設計判断である。