-
テーブル設計が悪いとプログラムの変更が大変になる
データの整理に失敗しているデータベース
- データベース設計がまずいと、データを適切に記録するためにテーブル定義やデータ内容に表れない暗黙知が必要となる
-
まずい設計例
- 用途がわかりにくいカラム
- いろいろな用途に使う巨大なテーブル
- テーブルの関連がわかりにくい
- 【補】リレーショナルモデルを勉強しろ、に尽きる
データベース設計をすっきりさせる
基本的な工夫を丁寧に実践する
-
名前を省略しない
- テーブル名やカラム名の文字数に厳しい制限があったのも今は昔
-
適切なデータ型を使う
-
大は小を兼ねる、はNG
- 無駄に大きな桁数
- TEXTやLOB
- データのドメインを適切に縛ることで不正データを防ぐ
-
-
制約をきちんと使う
- NOT NULL
- UNIQUE
- FK
NOT NULL制約が導くテーブル設計
-
【補】そもそもNULLが入った時点で第1正規形ですらない
-
第1正規形: 「テーブルがリレーションである」
-
必要条件
-
タプル(行)のアトリビュート(列)の値がアトミックである
- 簡単には1カラム1つの値、的な意味
-
NULLがない
- NULLはUNKNOWNまたはNOT APPLICABLE
- 「1カラム1つの値」に反する
- …
-
-
-
- 【補】リレーショナルモデルを勉強しろ(再掲)
- 【補】
accounts
テーブルにdeleted_at
(nullable)カラムを定義するのではなく、deleted_accounts
テーブルを定義するのがリレーショナルモデルとして正しいあり方
一意性制約でデータの重複を防ぐ
- 重複は異常(anomaly)につながる
- これを防ぐ
外部キー制約でテーブル間の関係を明確にする
- NOT NULL制約と一意性制約を徹底し、正規化していくと、テーブルは小さく多くなる
- ここにおいて、テーブル間の関係を明確にするために外部キー制約が重要
コトに注目するデータベース設計
業務アプリケーションの中核の関心事は「コト」の管理
-
4章再訪
- ドメインモデルはヒト/モノ/コトで整理
- モデリングにおける中核の関心事は「コト」
-
業務アプリケーションにおいては、コトを正しく記録し参照するためにデータベースが重要
- 現実に起きたコトの記録
- 将来起きるコトの記録
-
制約とコト
-
NOT NULL制約
-
起きたコト(事実。真となる命題)を記録するのにNULLは不適
- だってNULLの意味するところはUNKNOWNですから
-
-
一意性制約
- 参照時に苦しまない
-
外部キー制約
-
JOINして正しいデータを再現
- 【補】結合従属性
-
-
ヒトやモノとの関係を正確に記録するための3つの工夫
-
記録のタイミングが異なるデータはテーブルを分ける
-
さもないとNULLが生じる
- 「未定」
-
-
記録の変更を禁止する
-
取り消しデータと新データを登録する
- 会計と同じ
-
-
カラムの追加はテーブルを追加する
- 既存テーブルへのカラム追加は、NULLまたはNULL逃れの虚のデータを生む
-
新しくテーブルを追加し、新しいテーブルから既存のテーブルを外部キーで指す
- 【補】スタースキーマ的な
参照をわかりやすくする工夫
コトの記録に注力したテーブル設計の問題
-
多くのテーブルを結合する必要が出てくる
- 導出ロジックの複雑化
-
性能面の問題
- 【補】RDBMSはせいぜい4-5個テーブルのJOINまでを前提に設計されている
状態の参照
-
コトの記録だけで現在の状態は導出可能
- 現在の状態を参照しやすくするための冗長化は、データの重複や不正なデータの混入の温床
- とはいえ毎回導出するのは前述の問題がある
- どうする?
-
参照用の導出データも用意する
-
検索におけるインデックスと同様
- コトの記録のたびに更新
- あくまで原本は正規化されたデータ
-
UPDATE文は使わない
-
UPDATE文はデータの不整合が混入しやすい
-
記録の同時性に違反
- 【補】UPDATE前提のNULLカラムとかが生まれる
-
- 残高(状態)の更新には、代わりにDELETE/INSERTを使う
-
副産物: 冪等
- DELETE/INSERTではすでにレコードがあってもなくても操作できる
- cf. UPDATE方式は、新規登録時は代わりにINSERTする必要がある
残高更新は同時でなくてもよい
-
コトの記録と残高(状態)の更新は厳密なトランザクションとして処理されるべきではない
-
厳密に同時である必要なし
- 【補】結果整合性の話をしている??
- 数ミリ秒遅れても問題ないこと多し(要件依存)
- 二次的な「残高」の更新に失敗したからといって、本質である「コト」の記録も取り消すのはおかしい
-
-
同時でなくていいので、非同期メッセージングの可能性が出てくる
- システムの設計をシンプルにできる
残高更新は1ヵ所でなくてもよい/派生的な情報を転記して作成する
-
残高更新を複数のサーバで別々に行っても良い
- PUB/SUB
-
イベントソーシング
- コトの記録を情報源として、派生するさまざまな情報を目的別に記録する
-
メリデメ
-
メリ
- 参照系がシンプルになる
- 目的ごとに柔軟に修正や拡張ができる
-
デメ
- 厳密な即時性、派生データ間の整合性の保証には相応の仕組みが必要
-
コトの記録から状態を動的に導出する
-
副産物的なうれしいこと
- 将来のコトを擬似的に発生させることで、未来状態のシミュレーションにも使える
- 過去のある時点での状況を擬似的に作成し、テスト環境として利用できる
オブジェクトの設計とテーブルの設計
オブジェクトとテーブルは似ている
- 似てるだけ
違うものとして明示的にマッピングする
特性 | オブジェクト | テーブル |
---|---|---|
目的 | データとロジックの整理 | データの整理 |
関心事 | 導出や加工、判断 | 導出や加工の元となるデータ |
アプローチ | ボトムアップ(部品を組み立てる) | トップダウン(整合性のため) |
設計変更のリズム | 頻繁 | ゆるやか |
オブジェクトはオブジェクトらしく、テーブルはテーブルらしく/業務ロジックはオブジェクトで、事実の記録はテーブルで
- ドメインオブジェクトの設計にフレームワークの都合を持ち込まない
-
ORM
- オブジェクトとテーブルとは本質的に異なる
-
このことを踏まえて設計されたツールなら良い
- 例: SQL Mapper
-
ドメインオブジェクトのコードにテーブルを意識させるものはダメ
- 例: JPA: Java Persistence API
- 【所感】(依存の向き的に)Mapperじゃないってことですね
-
自動化するな
- ドメインオブジェクトの設計のためにもテーブル設計のためにも良くない