全データストアの現物調査。数値は 2026-09-14 に本番 Cloudflare を実測した値です。
図1: 関係データは D1、コンテンツ本体は R2。出題は DB でなく R2——2026-07-22 に D1 spi-questions-pool を削除し、current.json でバージョンを解決する不変リリース方式へ移行済み。
DB を使っていない領域: 過去問抽出・SPI バンク生成・論文バンク・WEB テキスト工房は JSON ファイル+ディレクトリ構造で管理(台帳部分のみ educare-content-ledger へ移行中)。求人クローラーだけは一次データがローカル SQLite(recruitment_v6.db・584MB・20表)で、公開分のみ D1 へ派生させる構成。
図2: LINE 色の強いテーブルと Educare 固有のテーブルが同じ DB に同居しているのは、この出自による。
| ドメイン | 主要テーブル |
|---|---|
| アイデンティティ | users(ハブ)/ user_identities(provider×uid で UNIQUE。line/email/google/spi_session)/ user_profiles / user_consents / friends |
| Web 認証 | user_sessions / email_login_tokens / web_login_codes / oauth_login_states / login_events / anon_sessions |
| LINE 配信(OSS) | line_accounts / tags / friend_tags / scenarios / scenario_steps / scenario_step_deliveries / broadcasts / auto_replies / messages_log / chats / rich_menu_* |
| 流入計測 | entry_routes / tracked_links / link_clicks / ref_tracking / conversion_points / conversion_events / ad_platforms / retro_attribution |
| 受験・演習 | spi_attempts(1受験=1行・answer_records は JSON)/ answer_events(1問1行・両建て)/ mondaishu_sessions / mondaishu_session_items |
| AI 模擬面接 | interview_sessions / interview_events / interview_costs / interview_access / interview_access_attempts |
| 教材・課金 | courses / lessons / course_revisions / offers / offer_items / learning_paths / learning_path_items / enrollments / user_plans / purchase_orders / stripe_events / stripe_member_events |
| 学習ログ | lesson_progress(状態)/ learning_events(出来事)/ learning_daily(日次集計) |
| メール・問合せ | mail_outbox / mail_send_attempts / mail_webhook_events / mail_audit_events / support_threads / support_messages |
| 運用 | voc_feedback / seminars / seminar_registrations / alert_subscriptions / municipality_events / system_flags / doc_intel |
図3: D1 が DB を跨いで JOIN できないことを、そのまま個人情報の堀として使っている。
図4: 2026-09-10 決裁。元は courses が価格と Stripe キーを直接持っており、セット売り・期間限定価格・同一教材の2価格が表せなかった。購入者ゼロのいま剥がす判断。
| 状態と出来事を分ける | 状態=lesson_progress(1人×1単元=1行)/出来事=learning_events・answer_events(追記専用)。集計は日次の learning_daily を読む。Open edX・Moodle と同型。 |
| 追加のみ(additive-only) | scripts/check-migrations.ts が CI で DROP/RENAME/ALTER COLUMN TYPE/DEFAULT 無し NOT NULL/ADD UNIQUE を機械遮断(041 以降)。down は無い。巻き戻しは D1 time-travel(DB 全体の時点復旧)。 |
| 論点 | 実態 |
|---|---|
| 主キー | 例外なく TEXT の UUID(crypto.randomUUID())。126 本に AUTOINCREMENT はゼロ。 |
| 時刻 | 🔴 2 系統が混在。065 以前=TEXT の JST 文字列(一部 UTC が混入)/067 以降=INTEGER unix ミリ秒。混在は実際にバグを生み、084/085 で文字列を剥がす回避が入っている。 |
| 外部キー | REFERENCES 宣言は 116 か所。新しい表ほど薄い——問題集(090)・プラン(094)・学習ログ(118) は user_id を素の TEXT で保持し FK 無し。整合はアプリ層任せ。 |
| UNIQUE | SQLite の制約上すべて CREATE UNIQUE INDEX で代替。状態つき一意性には部分 UNIQUE インデックス(UNIQUE … WHERE)を使う型が定着。 |
| JSON 列 | TEXT に JSON を詰める列が多数。json_valid() CHECK は 3 ファイル(029/040/064)のみ、大半は無検査。 |
| 論理削除 | deleted_at は予約系 2 本のみ。ほかは is_active フラグ。 |
| ORM | 不使用。素の prepare().bind()。原子性が要る所は db.batch()(D1 に BEGIN/COMMIT が無いため)。共通クエリは packages/db/src/(55ファイル)、ルート固有は routes 直書きの二層。 |
| SQL 組立 | 文字列連結は検出されず。値・JSON パスとも bind() 徹底、動的カラム名は固定リテラルのホワイトリスト。TypeScript の型は手書き(スキーマとの整合を機械検査する仕組みは無し)。 |
for migration in 108_materials 109_purchase_orders 110_stripe_member_events; do
pnpm exec wrangler d1 execute educare-poc-staging --remote \
--config apps/worker/wrangler.members-staging.toml \
--file "packages/db/migrations/$migration.sql" || exit 1
done
図5: 番号の重複 6 組(009・018・037・038・065 が各2本、041 は3本)、欠番 3(054・088・089)。ただし 088/089 は欠番ではなく別ブランチに実在する。
過去の事故の記録も残っている: 037_scenario_delivery_mode.sql は中身が SELECT 1; だけの空ファイル(先に手で本番へ当てた後 CI が再実行して duplicate column で落ち、「適用済みと記録するだけ」に書き換えられた)。096 のコメントにも「本番には別ブランチで 090_test_accounts.sql として当て済み。再適用するな」とある。
図6: メール系は別 Worker educare-mail が同じ D1 に対して持つ第2のマイグレーションディレクトリ由来。staging だけの 12 表は本番リリース前の教材・課金・学習ログ一式。
本番 Worker のコード反映は 3 段ゲート(prod_deploy.py execute → sudo promote → verify)で守られ、素の wrangler deploy は hook で遮断される。D1 マイグレーションの適用はこのゲートの対象外。
仕組みは完成(全D1を d1 export → gzip → age 暗号化 → オフサイト同期)。launchd 定義が .disabled のままリンクされ自動ロード対象外。launchctl list にヒット無し、ログの最終成功は 2026-07-22 17:10。
answer_events は本番稼働中(Worker が実際に INSERT/SELECT)だが CREATE TABLE が 126 本のどこにも無い。全ワークツリー横断検索で見つかったのは 2026-07-10 の本番ダンプの中だけ。設計ドキュメントから直接 d1 execute --command で当てたものと推定。
本番スキーマの全体像=「主 migration 126 本 + mail-worker の第2 migration + 他ブランチ限定の migration + 手当ての DDL」の総和(図5)。
Worker デプロイには 3 段ゲートと遮断 hook があるが、hook 17 本を全数検査して d1 execute に反応するものはゼロ。本番 DB は AI を含め誰でも直接書き換えられる。
本番にあって staging に無い表が 10 本(図6)。メール/問い合わせ系は staging で検証できない。
TEXT の JST 文字列と INTEGER unix ミリ秒が同居。additive-only 方針のため一括変換不可。
packages/db/schema.sql は 62 表のみで認証・教材・面接が丸ごと欠落。自動生成の DATA_MODEL.draft.md は 2026-07-01・65 番までで自称「未検証」。現時点で信頼できるのは教材限定の MATERIALS_DATA_MODEL_DESIGN_20260910.md のみ。