(更新: )
DBの外部キーはどこまでつけるか、組むならアグリゲートまで
はい、どうもこんにちは佐藤です!
名古屋の線状降水帯、みなさん大丈夫でしたか?
我が家はですね……無事に水没しました。車とバイクもきれいにやられまして、ただいま絶賛お仕事募集中でございます。ハハハ。
まあ、嘆いていても水は引かないので手を動かしますね。
そんな足元の不安な中、現場では 「DB の外部キーって、どこまでつけるの?」 という話が上がっていました。
正直に申し上げると、ある程度の規模まで行ったシステム開発って、もう宗教なんですよ。信じているものが人によって違うので、横から口を出したところで決着はつきません。宗派の違う相手に教義をぶつけたら、喧嘩になるだけですからね。
なので押し付けはしません。ただ、佐藤教の教義だけは置いていきます。
外部キーを組むなら、アグリゲートまで。拡張するなら、ドメインまで。
この意見には 3 本の柱があります。順番に書いていきますね!
「外部キーは遅くなるから付けない」で止まらないでほしい
まずは、いちばんよく聞く反対意見から潰しておきましょう。
「外部キーは制約チェックが走る。だから遅くなる。だから付けない」
理屈としては正しいです。参照先の存在確認は確かにコストですし、インデックスやロックの話まで含めれば、ゼロではありません。
ただ、聞かせてください。そのシステム、何件のデータを扱う予定ですか?
一企業の業務システムです。受注テーブルが年間で数万行、多くても数十万行。マスタに至っては数千行といったところでしょう。その規模で外部キーを外したところで、ユーザーが「お、速いな」と感じるところまで改善しません。
体感が変わらない改善のために、あとで説明する 2 つの資産を捨てている。これが、私がいちばんもったいないと感じる判断です。
もちろん、秒間に数千件が飛んでくるようなシステムなら話は別ですよ。そこは宗教ではなく物理なので、外してください。でも、その手の要件はそんなに転がっていません。
「速いかもしれない」という想像のために、確実に手に入るものを手放していないか。まずはそこを疑ってほしいのです。
外部キーの本当の仕事は、ER 図のリバースです
では、外部キーを張ると何が手に入るのでしょうか。
世間が語るのは、だいたい整合性の話ですよね。親のいない子レコードが生まれない、消し忘れが起きない、と。もちろんそれも正しいです。
ただ、語られない方にこそ価値があると思っています。
ER 図のリバースです。
A5:SQL Mk-2 のような優秀な DB ツールを使えば、実際の DB から ER 図を起こせます。テーブルを読み込ませれば、リレーションの線まで引いた図が出てくるわけですね。
これ、外部キーが張ってあることが前提なんですよ。
私は以前、外部キーがひとつも無い DB をリバースしたことがあります。出てきた画面を今でも覚えています。**箱です。**線が 1 本も繋がっていない箱が、画面いっぱいに並んでいるのです。
あれは絶望しました。どのテーブルがどのテーブルにぶら下がっているのか、まったく読み取れません。
ここで、こう思いませんでした?
「線が無くても、テーブル名を見ればだいたい想像つくでしょ」
つくと思いますか。古いシステムの DB は暗号ですよ。
こういうやつです。
T_JCH001
M_KSN
W_TRN_BK2
T_JCH001_OLD
T_JCH001_OLD_BK
T_JCH001 の中身が何なのか、想像つきますか。私はつきませんでした。有識者は退職済みです。
カラムに降りても救われません。CD1、CD2、KBN、FLG、YOBI1〜YOBI5。予備カラムが 5 本ある時点で、この DB が何度も殴られてきたことだけは伝わってきます。
KBN が 3 のときだけ CD2 が別の意味になる、みたいな仕様がどこかに埋まっているわけですね。そしてその仕様は、ER 図でもドキュメントでもなく、帳票を出すバッチの分岐に書いてあります。
結局、私がやったのはこういう作業です。テーブルを片っ端から開いて、実データを目で見て、「この 8 桁の数字、隣のテーブルの主キーと桁数が同じだな……」と当たりをつける。実際に結合してみて、件数が合うかどうかで答え合わせをする。
考古学です。設計をしていません。データを掘っているだけです。
これを 40 テーブル分やりました。その間、機能はひとつも増えていません。
外部キーが 1 本張ってあれば、この作業はツールが一瞬で終わらせていたのです。
外部キーは、未来の誰かが仕様を理解する速度そのものなんです。
そして、その未来の誰かは、たいてい自分だったりします。
ちゃんとメンテされた ER 図、ありますか?
ここで、こう思った方がいるはずです。
「ER 図なんて、設計書として自分で書けばいいじゃん」
はい、正論です。正論なのですが、胸に手を当てて思い出してほしいのです。
ちゃんとメンテされた ER 図、見たことありますか?
私はあります。ただし、作った直後の 1 回だけです。
リリースを 2 回ほどまたぐと、カラムが足りなくなって現場で追加され、中間テーブルがこっそり増え、使われなくなったテーブルが墓標のように残ります。そして ER 図は更新されません。誰も怒られないからです。
ドキュメントが嘘をつき始めると、次に入った人はドキュメントを信じなくなります。信じられないドキュメントは、無いのと同じどころか、読んだ時間の分だけマイナスですよね。
だから私は、コードが正という立場に落ち着きました。DB でいえば、実際に動いている DDL が正です。
正であるものから機械的に起こせる図。それがリバースした ER 図であり、そのために外部キーを張っておく。**外部キーは、ほとんどリバースのためにある。**私はそこまで思っています。
テストデータを入れるときに投入順序を気にしなければならないとか、そういう面倒はあります。正直、テストの効率は落ちます。でも、落ちるのは作っている数ヶ月の効率です。ER 図が読めないことで損をするのは、その後の数年なのです。
このあたりの「後から一番戻しにくいのは DB だ」という感覚は、以前 AI 時代に求められるのは、DB 設計のカードを何枚持てるかだ にも書きました。
どこまで張るのか。答えは「担当範囲」です
さて、ここからが本題です。
外部キーが効くのは分かった。ではどこまで張るのか。選択肢は 3 つありますよね。
| 範囲 | 内容 |
|---|---|
| アグリゲート単位 | ひとかたまりの中だけ縛る |
| ドメイン単位 | 業務のまとまり全体を縛る |
| 全体 | すべてのリレーションを縛る |
私の答えは、担当範囲で分けるです。
1 人(あるいは 1 チーム)が責任を持って面倒を見る範囲の中は、外部キーで固く縛る。担当が分かれる境界をまたぐキーは、張らない。
理由は単純で、他人の都合で自分の作業が止まるのが最悪だからです。
境界をまたいでキーが張ってあると、隣のチームがマスタの持ち方を変えた日に、こちらのマイグレーションが落ちます。こちらは何も悪くないのに、朝会が謝罪会になるわけですね。逆に、こちらが急ぎでテーブルを分割したいときも、隣に頭を下げて回る必要が出てきます。
縛るべきは、自分で責任を取れる範囲まで。
そして、その「責任を取れる範囲」は、たいていアグリゲートの単位と一致します。ひとかたまりで生まれて、ひとかたまりで消える。そういう塊です。アグリゲートという言葉が馴染まない方は、画面 1 枚で承認が下りる単位とでも読み替えてください。この考え方そのものは ドメイン駆動開発の革新は、アグリゲートだったのではないか に詳しく書いたので、気になる方はそちらもどうぞ。
なので、大体はアグリゲート単位に落ち着くはずです。
小さいシステムなら、ドメインまで広げてしまって構いません。担当者が 1 人か 2 人なら、境界をまたいでも困る相手がいませんからね。むしろ縛れるだけ縛った方が、あとで読むときに楽です。
全体に張って詰んだ話
では、全体に張るのはどうでしょうか。
**やめたほうがいいです。**私がやって詰んだので。
以前、整合性は固ければ固いほど良いと信じていた時期がありまして、思いつく限りのリレーションに外部キーを張ったことがあります。マスタからトランザクション、ログの類いまで、全部です。
最初は気持ちよかったんですよ。データがきれいに入りますし、変なレコードが生まれません。
地獄はマイグレーションで来ました。
テーブルをひとつ分割しようとすると、そこにぶら下がった制約を全部外して、順番を考えて作り直して、また張り直す。依存が数珠つなぎになっているので、1 箇所直すのに 10 箇所が動きます。リリース手順書が、儀式の式次第みたいになっていきました。
データ投入も辛かったです。初期データを流すのに、どのテーブルから入れるかを解く作業が発生します。これ、パズルを解いているだけで価値は 1 円も生んでいないんですよね。
結局、あのとき欲しかった整合性は、アグリゲートの中だけで足りていたのです。塊をまたいだところの整合性は、アプリケーション側で見ればよかった。
広げすぎると、守るためのものが足枷になります。
だから、組むならアグリゲートまで。拡張するとしても、ドメインまで。ここが私の線です。
業務アプリが守るのは、速さではなく整合性と拡張性
最後の柱です。これは価値観の話になります。
業務アプリケーションで優先すべきは、パフォーマンスよりも、拡張性と整合性だと思っています。
なぜかというと、業務アプリは変わり続けるからです。
税制が変わります。組織が変わります。あの部長が異動してきて承認フローが増えます。5 年動いているシステムで、初期の仕様のまま残っている画面がどれだけあるでしょうか。
つまり、業務アプリの寿命のほとんどは改修に使われるわけですね。ならば、改修しやすい状態を守るものに投資するのが筋です。
そして、改修で最初に読むものは何でしょうか。データ構造ですよね。
処理は読み飛ばせます。画面も後回しにできます。でも、データの持ち方を誤解したまま手を入れると、必ず事故ります。だから最初に ER 図を見る。だから外部キーが要る。ここまでの話は、全部ここに繋がっています。
一方で速さはどうかというと、先ほど書いたとおり、一企業のデータ量では**外部キーの有無で体感が変わりません。**変わらないものを優先して、変わり続けるものへの備えを削る。これは順番が逆なんじゃないでしょうか。
速さが効くのは、遅くて業務が止まっているときだけです。そうでないなら、守るべきは別のところにあります。
まとめ
というわけで、外部キーを組むならアグリゲートまで、拡張するならドメインまでだよ!って話でした。
3 本の柱をもう一度並べておきますね。
- 外部キーの本当の仕事は、ER 図のリバース。整合性はむしろおまけ
- 張る範囲は、担当範囲で決める。自分で責任を取れるところまで
- 業務アプリが守るのは、速さではなく整合性と拡張性
冒頭に書いたとおり、これは教義です。宗派が違う方に改宗を迫るつもりはありません。
ただ、ひとつだけお願いがあります。外部キーを外すと決めるときに、自分が何を捨てているのかだけは自覚してください。
捨てているのは、制約チェックの数ミリ秒ではありません。5 年後にこの DB を引き継いだ誰かが、仕様を理解するまでの時間です。その誰かは、あなたかもしれませんし、あなたが採用した若手かもしれません。
自覚した上で外すなら、それは立派な設計判断です。何も言いません。
ぜひみなさんも、一度いまの案件の DB をリバースしてみてください。線が繋がっていたら、過去の自分がいい仕事をしていた証拠ですよ!