/note/tech
■ 1. 独立した削除・無効化テーブルへの分離設計
- 設計の概要:
- 論理削除はメインテーブルに
deleted_at 列を持つ方式
- 分離設計はメインテーブルをアクティブデータのみとし、削除情報を専用テーブルに分離する方式
- メリット:
- メインテーブルにアクティブデータのみ存在するため、
WHERE deleted_at IS NULL の付け忘れが原則ゼロになり、インデックス効率も向上する
- 削除理由・操作者・承認者など削除イベント固有の属性をメインテーブルを汚さずに保持できる
- 削除テーブルを参照するだけで削除イベントの全履歴を把握でき、監査・追跡が容易になる
- 削除済みレコードがメインテーブルに蓄積しないため、長期運用でのテーブル肥大化を抑制できる
- 「削除テーブルから行を削除=復元」として操作が明確になり、復元ロジックを独立管理できる
- デメリット:
- 削除済みを含む全件表示や削除ユーザー参照の処理でJOINが必須となり、実装複雑度が増加する
- メインテーブルから行を物理的に削除する場合、参照している子テーブルのFK制約を先に処理する必要がある
- 子テーブルが削除済みの親を参照するケースで、アーカイブテーブルへの参照設計が必要となり複雑化する
- ActiveRecord・Hibernateなどの論理削除サポート機能が使えなくなり、独自実装が増加する
- 「メインテーブルから削除 → 削除テーブルへINSERT」を原子的に行う必要があり、実装ミスのリスクがある
- 使い分けの指針:
- 削除件数が多くパフォーマンスが重要、または削除メタデータが必要な場合は分離テーブルが適する
- 削除済みを頻繁に参照する、またはORM標準機能を活用したい場合は論理削除が適する
- GDPRなど物理削除が法的に必要な場合は分離設計にパージジョブを組み合わせる
■ 2. 状態の個別テーブル表現の評価
- 概念的な特徴:
- 各テーブルが単一の状態・意味を持つ高凝集な設計であり、状態ごとのメタデータを自然に保持できる
- 集合論的に「状態=集合への所属」として表現できるため、概念的な純粋さがある
- 過剰設計になる理由:
- 状態は排他的であるにもかかわらず、テーブル分離はスキーマ上で排他性を保証できず、アプリ層での担保が必要になる
- 「現在の状態はどれか」を取得するために複数テーブルへのJOINが必要となり、クエリコストが高くなる
- ビジネス要件の変化に伴い状態数が増えるたびにテーブルが追加され、マイグレーションコストが線形増加する
- 現実的な落とし所:
- メインテーブルに
status 列と最低限のメタデータ列を持ち、状態変更イベントを別のログテーブルに記録する方式が堅牢
- 現在状態はメインテーブルで高速に取得でき、排他性はスキーマで保証され、履歴・監査はログテーブルで追跡できる
■ 3. テーブル分離が正当化される条件
- 状態が排他でない場合:
- 「集合への所属」が自然なモデルとなるため、テーブル分離の概念と一致する
- ユーザーの資格・ロールのような複数同時保持が可能な属性が該当する
- 状態ごとの属性が本質的に異なる場合:
- 状態固有の業務属性が多く構造レベルで異なる場合、ポリモーフィズム的な分離に意義が出る
- 状態を集合として頻繁に集計・結合する場合:
- 状態テーブルがクエリの起点となるユースケースが多い場合、独立テーブルの方がインデックス設計しやすい
- 状態に独立したライフサイクルがある場合:
- 状態が「点」ではなく「期間」や「プロセス」として存在する場合、それは別エンティティとして扱うべき
- 他テーブルからFK参照される場合:
- 状態そのものを他テーブルからFK参照する必要があるなら、状態はエンティティとして独立させるべき
- 同一状態の履歴が複数回発生しうる場合:
- 最新の状態だけでなく状態の発生履歴が業務上意味を持ち、1エンティティにつき複数行が必要なケースが該当する
- 判断の本質:
- 「状態」が名詞として業務語彙に登場し、それ自体が属性・関連・ライフサイクルを持つなら分離する
- 動詞や形容詞レベルの状態であれば列で十分
■ 4. 中心エンティティの状態テーブル化の問題
- 中心エンティティへの横断参照:
users・orders・products のような中心エンティティはどの状態でも参照されることが多く、状態でテーブルを分けると参照元が状態を意識しなければならない
- 現在状態と履歴の混在による設計破綻:
- 状態テーブル化は現在の状態しか表現できず、「過去に停止されたアクティブユーザー」のような履歴が必要になった時点で設計が破綻する
- 結局メタデータテーブルが必要になり、状態テーブルの存在意義が消える
- 正当化される条件:
- 中心エンティティではなく、状態そのものが業務エンティティである場合に限られる
- 「従業員の在籍状態」ではなく「雇用契約」、「サブスクの停止状態」ではなく「一時停止イベント」のように、状態テーブルとして見えても実際には独立したエンティティである場合が該当する
- 結論:
- 中心エンティティを状態でテーブル分割する意義はほぼない
- 「状態テーブル化」という概念自体が、エンティティと状態の混同から生まれた設計パターンである可能性がある
■ 5. 製造物管理における状態テーブル分離の正当性
- 正当化される理由:
- 調達・製造・品質管理・倉庫・物流が各テーブルだけで業務を完結でき、管理主体ごとの組織的分離が明確
- 状態ごとにNULLだらけのスパーステーブルになることを避けるため、スキーマが状態分離を要求している
- 状態遷移が一方向(調達→加工→検品→保管→出荷)であり、前状態への参照需要が低い
- 各状態テーブルがKPIやオペレーションの集計単位と直接一致する
- 残る設計上の問題:
- トレーサビリティのためには製品エンティティを中心テーブルとして残し、各状態テーブルが
product_id で紐づく設計が必要
- 手戻り・再検品が発生すると「状態は一方向」という前提が崩れ、別途設計が必要になる
■ 6. 状態テーブル分離と相性の良いビジネスドメイン
- 適合度が最高のドメイン:
- 物流・サプライチェーン(入荷→仕分け→保管→ピッキング→梱包→出荷)
- 医療・臨床試験(患者登録→スクリーニング→治療→経過観察→退院)
- 金融・ローン審査(申込→書類審査→与信審査→承認→契約→実行)
- 適合度が高いドメイン:
- 建設・不動産開発(用地取得→設計→許認可→着工→施工→検査→竣工)
- 適合度が中程度のドメイン:
- 採用・人材管理(手戻りや並行選考が発生しやすく、一方向性が弱い)
- 適合度が低〜中程度のドメイン:
- EC・受注管理(キャンセル・返品が頻繁で一方向性が弱く、顧客の横断参照需要も高い)
- 適合の本質:
- 業務プロセスの境界が組織や規制によって外部から強制されているかどうかが適合度を決定する
■ 7. スケールとシステムアーキテクチャへの発展
- 状態テーブル分離とマイクロサービスの等価性:
- 状態テーブル分離が正当化される条件(管理主体・組織・規制の境界がフェーズと一致)は、本来別システムが管理すべき領域と一致する
- 状態テーブル分離が美しく見えるのは、本来マイクロサービスで解決すべき問題をDB設計で解こうとしているためである可能性がある
- 単一DBに留まる現実的理由:
- システム構築コスト、組織の未成熟、トランザクション整合性の要件、スタートアップ期の過剰設計回避
- 単一DBのまま成長した場合の問題:
- スキーマ変更が困難になり、チーム間でテーブルの所有権が曖昧になる(Conway's Law)
- 現実的な移行パス:
- モノリス単一DB(初期)→ モジュラーモノリス・スキーマをドメインで名前空間分割(中期)→ マイクロサービス(大規模・組織成熟後)
- モジュラーモノリスが状態テーブル分離の正しい着地点になるケースが多い
■ 8. 結論: 状態テーブル導入の判断軸
- 無邪気な状態テーブル導入の問題:
- 状態テーブルを導入したくなる衝動(属性の違い、管理者の違い、集計の必要性)はドメイン分離を要求しているサインであり、DBで解決しようとするから設計が歪む
- 状態テーブルへの衝動と正しい解:
- 状態ごとに属性が異なる → ドメイン・エンティティの分離
- 状態ごとに管理者が異なる → システム・モジュールの分離
- 状態ごとに集計したい → 各ドメインが自分のDBを持つ
- 状態間の整合性が怖い → イベント通信・結果整合性で扱う
- アンチパターンの定義:
- アンチパターン: ドメイン分離を検討せず、複雑さをDB設計で吸収しようとすること
- アンチパターンではない: ドメイン分離を検討した上で、コスト・規模・組織の成熟度から意図的に単一システムに留め、状態テーブルを選択すること
- 最終的な判断軸:
- 状態テーブルを導入したくなった際はまず「これはDB設計の問題か、それともドメイン境界の問題か」を問うべき
- その問いを経由せずに状態テーブルを導入するのはアンチパターンであり、経由した上での選択なら制約の中での合理的なトレードオフになる
関連:
(2026/05/30)