DBの外部キーはどこまでつけるか、組むならアグリゲートまで
DBの外部キーはどこまでつけるか、組むならアグリゲートまで

(更新: )

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 本の柱をもう一度並べておきますね。

  1. 外部キーの本当の仕事は、ER 図のリバース。整合性はむしろおまけ
  2. 張る範囲は、担当範囲で決める。自分で責任を取れるところまで
  3. 業務アプリが守るのは、速さではなく整合性と拡張性

冒頭に書いたとおり、これは教義です。宗派が違う方に改宗を迫るつもりはありません。

ただ、ひとつだけお願いがあります。外部キーを外すと決めるときに、自分が何を捨てているのかだけは自覚してください。

捨てているのは、制約チェックの数ミリ秒ではありません。5 年後にこの DB を引き継いだ誰かが、仕様を理解するまでの時間です。その誰かは、あなたかもしれませんし、あなたが採用した若手かもしれません。

自覚した上で外すなら、それは立派な設計判断です。何も言いません。

ぜひみなさんも、一度いまの案件の DB をリバースしてみてください。線が繋がっていたら、過去の自分がいい仕事をしていた証拠ですよ!

この記事をシェアする