はじめに
そのDB設計、壊れてはいない。でも、どこか怖い。
リプレース案件に携わっていると、
ときどき「一見すると不思議なDB設計」に出会います。
- テーブルの中にテーブル名やカラム名が入っている
- 値はすべて別テーブルに格納されている
- スキーマ変更せずに項目を増やせる
可用性は高そうに見える一方で、
「セキュリティ的に大丈夫なのか?」
という違和感を覚えることも多いでしょう。
この設計には名前があることを学びました。
それが EAV(Entity-Attribute-Value) です。
EAVとは何か
EAVとは Entity / Attribute / Value の略で、
「エンティティ・属性・値」を分離して管理するデータモデルです。
通常のテーブル設計
User
- user_id
- name
- age
- email
EAV設計
Entity
- entity_id # エンティティ固有のID
- entity_type # エンティティの種類(User, Product など)
Attribute
- attribute_id # 属性固有のID
- attribute_name # 属性名(name, age, email など)
Value
- entity_id # どのエンティティの値かを示す
- attribute_id # どの属性の値かを示す
- value # 実際の値(文字列・数値・日付など)
通常は「カラム」として持つ情報を、 EAVでは 行として保持 します。
一言で言うと 「カラムを行にした設計」
です。
なぜEAVは生まれたのか(当時の前提)
EAVは思いつきで生まれた設計ではありません。
当時の前提条件では、非常に合理的でした。
当時の技術的背景
- スキーマ変更が高コスト
- 再デプロイが難しい
- アプリサーバが貧弱
- ORMやマイグレーションが未成熟
業務上の要求
- 顧客ごとに項目が異なる
- 運用中に項目を追加したい
- パッケージとして提供したい
これらを満たすために
「スキーマを固定しない」
という選択肢としてEAVが採用されました。
当時は正解だった理由
EAVには明確なメリットがあります。
| メリット | 説明 |
|---|---|
| カラム追加が不要 | 項目を増やすたびにスキーマを変更する必要がない |
| 設定変更だけで拡張可能 | 顧客固有の項目にも対応しやすい |
| 管理画面と相性が良い | UI側で柔軟に対応できる |
| 汎用基盤を作りやすい | 共通モデルとして扱いやすい |
2000年代前半においては、
「変更に強い、賢い設計」
として評価されていました。
なぜ“怖い設計”になったのか
時代が進むにつれ、EAVの弱点が顕在化します。
クエリが複雑になる
- JOINが増える
- 可読性が低い
- 意図が読み取れない
型安全性が失われる
- 値は文字列で保存されがち
- 数値・日付の保証がない
- バリデーションが分散する
パフォーマンス問題
- インデックスが効きにくい
- 集計・検索が遅い
セキュリティ・監査リスク
- 内部構造(メタ情報)が露出する
- 動的SQLを使いがち
- 権限制御が破綻しやすい
ORM・静的解析との相性
- エンティティを定義しづらい
- LINQが書けない
- レビューや解析が効かない
具体例: 「ユーザーごとの動的項目を LINQ で安全に取得できない」など
認識の変遷
| 時期 | 評価 |
|---|---|
| 当時 | 先進的で賢い設計 |
| 運用期 | 便利だが運用が辛い |
| 後期 | アンチパターン認定 |
| 現在 | 用途限定でのみ適用 |
EAV自体が悪になったのではなく、
前提条件が変わった
ことが大きな理由です。
現代におけるEAVの適切な使いどころ
使ってもよいケース
- 設定データ
- メタ情報
- カスタム項目(低頻度)
- 監査・ログ
使うべきでないケース
- 主要業務データ
- 検索・集計が多い領域
- 型安全・セキュリティ重視の箇所
リプレース案件での現実的な扱い
- 新規設計では採用しない
- 既存EAVは隔離・縮小
- ViewやAPIで境界を作る
- 動的SQLを排除する
「全面否定」ではなく、
影響範囲を限定する ことが重要です。
まとめ
- EAVは当時の前提では正解だった
- 現在の前提ではリスクが大きい
- 設計の評価は時代とともに変わる
過去の設計を否定するのではなく、
「今の前提でどう扱うか」
を考えることが、リプレース案件では最も重要です。