以下、数ヶ月もんもんと考えていてなかなかアウトプットすることができなかったので自分語り交えながらとりあえず出力することにした。 読みづらいのは勘弁願いたい。
なるほど、これがDDDとしての模範解答なら僕には数ヶ月考えてもたどり着けなかったのに納得がいく
数ヶ月ほど前、アドベントカレンダーに参加したときに以下の記事を書いた。
集約の境界と整合性の維持の仕方に悩んで2ヶ月ぐらい結論を出せていない話 - kbigwheelのプログラミング・ソフトウェア技術系ブログ
そうしたところとても多くの人に反応を頂いて、自分としても一応の結論を出すことができた。
「集約の境界と整合性(略」に対して頂いたアイデアの分類と現状での僕の回答らしきもの - kbigwheelのプログラミング・ソフトウェア技術系ブログ
このとき多くの人の意見を読ませてもらい自分の結論に至って考えたのは、この結論は自分一人で考えていては絶対にたどり着けなかったということだ。
僕がどの解法を自分の結論としたかは上の記事を見てもらうとして、なぜ自分一人ではたどり着けなかったというとこの結論が正規化していないテーブル構造だからだ。
テーブル構造の正規化
テーブル構造の正規化、およびそのパターンについては他の記事・サイトに説明を譲るとして*1、 使わないほうが良い説もそこそこ聞く外部キーと比べてDBの正規化がアンチパターンだと語る話はほとんど聞いたことがない。 テーブルの正規化はRuby系のDRYにも似た概念であり、無駄なデータの重複を作らないことは更新漏れを予防したりデータ保存効率を上げたりする点でも勧められるよい設計方針だ。 RDBMSでの正規化はOOP系のプログラミング言語とのインターフェイスでインピーダンスミスマッチを起こす要因として 挙げられることはあるものの、我々プログラマーはRubyのActiveRecordやORマッパーの機能、時には自分たちでDAO/DIOを書くことで そのミスマッチをプログラム側の努力で解消してきた。 それはそれだけDB領域の正規化を重要視してきたからである。
通常1対多のテーブル関係がある場合、多の方のカラムに1の方のIDを持たせるだけでよい。 データ保存効率がよく、また間違って多対多の関係になってしまうことも防げる。良いことづくめである。 たびたびで恐縮だが、上記リンクの回答編、最終的な結論ではDBを非正規化する解法を選択している。 なるほど、その選択肢は僕の頭には一切なかった。
なぜRDBMSを使用しているにもかかわらず非正規化しているのか
端的に言えばトランザクション境界をスパッと切るためである。 上の例で言うと組織集約とユーザー集約は別の集約(=リポジトリ)でありトランザクション境界で別れている。 DB正規化の観点からすればどのユーザーがどの組織に属しているかはユーザー側のテーブルで管理するべきだが、 オブジェクト指向、というかDDDの集約(リポジトリ)の見地からすればそれは組織集約の方で管理するべきなのだ。 1対多の関係が多対多になるリスクを負うことになるが、それでもである。
結果整合性を使うということはDBで正規化を捨てるということだと君は理解しているか
私は今までごく少数の例外(forgeign keyや論理削除・物理削除)などの問題を除けばRDBMSの基本原則を信奉していたし、 インピーダンスミスマッチを認めつつもそれはプログラム側で努力して解消するもの、または工夫により解消できるものと信じてきた。
特に正規化については重複を避けるプログラミングの原則にもマッチしているし、RDBMSを使う根本理由であるとすら思っていた。
その考えが今揺らいでいる。
慣習的に従いたいのは正規化の方針だ。しかしDDDとIDDDを読み、多くの先達の意見を聞いた今理性は正規化を捨てるときだと言っている。 実際そういった思いでDDD・IDDDのリポジトリ・トランザクションの章を読むと正規化を捨てるという案は全編で肯定されている。 DDDに従うならば、必要に応じて正規化を捨てるという手段は当たり前のもののようである。
しかし、思うのだ。 我々は主にウェブプログラミングへマッチさせるために外部キーを捨て、論理削除の概念を生み出した。 この上正規化を捨てるとなるともはや使っているRDBMSの機能のほうが少なくなってくる。 集約でトランザクション境界を切るとなるとJOINすることも稀になるからだ。 もはやこの用途ではRDBMSよりスキーマ強制されるNoSQLを使うほうが用途にあっている気さえする (CQRSでJOINをフル活用する必要がある場合はRDBMSが生きると言えるが、あれは邪道な気がしてどうにもまともに捉える気になれない)。
DDD本にも何度か出てくるが過去オブジェクト指向DBというものが何度も考案されては実用化できずに消えていった。 しかし、このDDDに沿ったリポジトリ実装だとどうにもこのオブジェクト指向DBというものが最適な気がしてならない。
我々は、ドメイン領域と外界とのインピーダンスミスマッチを最小にするため、ソフトウェアエンジニアリング史何度目かのオブジェクト指向データベースへの挑戦をするべきなのではないだろうか。
蛇足
今回の経験で、結果整合性を使うために集約間でトランザクション境界を切る場合は、それぞれのテーブルを別のスキーマ(MySQLなんかだと複数スキーマ間でJOINなどできてしまうが一旦それはなしと考えて)に置くとしてテーブルを設計すればいいことに気がついた。
*1:実際のところ他人に説明できるほど理解していないのである