ドメイン駆動開発の革新は、アグリゲートだったのではないか
ドメイン駆動開発の革新は、アグリゲートだったのではないか

ドメイン駆動開発の革新は、アグリゲートだったのではないか

はい、どうもこんにちは佐藤です!

また X でクリーンアーキテクチャがどうのこうのと話題になっていました。

銀の弾丸は無いのですから、ああいう議論になるのは当然だろうなと思って眺めています。効く現場と効かない現場があって、どちらの人も自分の現場では正しい。それだけの話ですよね。

ただ、そこでふと考えてしまいました。じゃあ私は、何を見て作りを決めているんだろう?

振り返ってみると、私の判断はかなりはっきりしています。そして、その根っこにあるものを掘っていったら、思いがけないところに着きました。

ドメイン駆動開発の革新的な部分は、アグリゲートの考え方だったのではないか。

そしてその正体は、20 年前に授業で聞いて、まったく理解できなかったあの概念でした。順番に書いていきますね!


私はこう作り分けている

まず、実際の判断から。私の作り分けはこうなっています。

判断作り
テストが有効と判断したらオニオン
有効でない、メンテできないと思ったら4 層
単純な CRUD なら3 層

これは以前 4層かクリーンアーキテクチャかは、使うDBが決める で書いた話とほぼ同じですね。あの記事では、4 層とオニオンの差は結局モックテストをやるかどうかで、その価値は使う DB が決める、という結論を出しました。

その考えは今も変わっていません。ただ、もう一段下があったのです。

テストが有効かどうかを判断する前に、私は無意識にもっと手前で分岐していました。そこにアグリゲートがあるかどうかです。


分かれ目は、アグリゲートの有無だ

アグリゲートとは何か。私の理解はこうです。

エンティティをまとめたもので、状態とイベントを定義して、そのエンティティを操作するもの。

抽象的ですよね。実案件で説明します。

アグリゲートが要ると判断したのは、ワークフローシステムでした。

起票して、申請して、承認して、終了する。**ステータスが遷移していきます。**そして遷移のきっかけになるイベントがあり、遷移していいかどうかのルールがある。申請中のものをいきなり終了にはできませんよね。

こういうものは、明細とステータスをまとめて 1 つの塊として扱わないと破綻します。「明細を変えたら、このステータスに戻す」という整理が要るからです。

逆に、要らないと判断したのは、管理者ひとりのマスタ管理システムです。

承認もなければ、状態遷移もありません。触るのは管理者ひとり。ただ、DB を手入力させるのは危険なので、**正規化のルールだけアプリケーション側に仕込みました。**それで十分だったのです。

この 2 つの違いを一言でいうと、包むものがあるかどうかでしょうか。

ワークフローには、守るべき整合が塊として存在します。マスタ管理にあるのは正規化のルールだけで、**アグリゲートを作っても包むものがありません。**空の箱を作ることになります。

判断の順番はこうです。

  1. アグリゲートが要るか(状態とイベントを持つか)
  2. テストが有効か(DB が何か)

1 で要らないなら 3 層でいい。要るなら、2 の判断に進みます。前回の記事は、この 2 段目だけを書いていたわけですね。


アクティブレコードでは、なぜアグリゲートが作れないのか

ここでフレームワークの話になります。

**アクティブレコードは、原則ひとつのエンティティにしか設定できません。**テーブル 1 枚がクラス 1 個で、そのクラスが自分の保存も知っている。手軽で、書き味は最高です。

ただ、他のエンティティとのコラボレーションが難しい。

先ほどのワークフローで考えてみましょう。明細のエンティティと、ステータスのエンティティがあります。アグリゲートなら、明細を変えたらこのステータスにするというルールを塊の中に書けます。外から見れば「明細を変えた」だけで、整合は中で守られる。

アクティブレコードだとどうなるでしょうか。明細を保存して、それからステータスを更新して……という手順を**呼び出す側が知っていないといけません。**つまり、その影響がアプリケーション層に渡ってしまうのです。

そして、ここが厄介なところで、**アプリケーション層に漏れたルールは、必ず複数箇所に散ります。**画面から呼ぶ経路、バッチから呼ぶ経路、API から呼ぶ経路。どこかで書き忘れたら、そこだけ整合が壊れます。

だから、フレームワーク選定でここが大きな判断になるのです。

Laravel や Django が悪いという話ではありません。**あれらはアクティブレコードを前提に、最高に使いやすく作られています。**マスタ管理のような、包むものがないシステムなら圧倒的に速い。

ただ、承認フローのある業務システムを、あの作りのまま素直に書いていくと苦しくなります。「クリーンアーキテクチャに寄せた作りになっていないな」という違和感の正体は、突き詰めるとアグリゲートが作れないという一点だったのだと思っています。


これ、カプセル化じゃないか

ここまで書いていて、ハッとしました。

これ、カプセル化じゃないか。

オブジェクト指向では、中身のパラメータは隠蔽して、外から操作できるメソッドを用意するのが王道の作りですよね。直接いじらせない。必ず窓口を通す。そうすれば、中の整合はクラスが自分で守れます。

アグリゲートがやっていることは、まったく同じです。違いは、隠蔽している中身がパラメータではなくエンティティになったという、それだけ。

イベントを受け取って、中のエンティティをいじる。これはカプセル化の概念そのものです。

正直に告白すると、カプセル化なんて、高校時代に触れて全く理解できなかった概念でした。「変数を private にしてゲッターとセッターを書きましょう」と言われても、何が嬉しいのかまるで分からない。社会人になってからもフロントばかりやっていたので、あまり縁がありませんでした。

メモリ上に猫を用意するってなんぞや!って

それがバックエンドをやるようになって、業務の整合を守る仕事をするようになって、ようやく腑に落ちたのです。20 年越しの答え合わせをしてもらった気分だぜ、というのが正直なところですね。

ドメイン駆動開発、よくできているのだなぁと感心しますわ。


なぜ DB が入ると、その感覚が消えるのか

ただ、ひとつ疑問が残ります。

カプセル化なんて基本中の基本なのに、なぜ DB が絡んだ途端に、その感覚がどこかへ行ってしまうのでしょうか。

ここからは推測でしかないのですが、DB がソースコードになっていく歴史なのだろうと思っています。

もともと DB は外部記憶でした。ソースとは別物だったのです。プログラムの外側にデータの置き場があって、そこに命令を送って出し入れする。カプセル化の対象になりようがありません。中身が外にあるのですから。

そこに **ORM が来ました。**DB のレコードが、ソースの一部として機能するようになります。テーブルがクラスとして見えるようになった段階ですね。

さらに**クリーンアーキテクチャなどで、差し替え可能なものとして扱う概念が増えました。**インターフェースの向こう側に置かれ、いよいよソースコードの語彙で語れるようになります。

そうやって歩みを進めるうちに、DB がソースコードとして扱えるようになった。

つまり、カプセル化の感覚が消えていたのは、DB が「外の別物」だった時代の名残だったのではないでしょうか。距離が縮まりきった今だからこそ、エンティティを包むという発想が自然に効くようになった。そう考えると、アグリゲートが 20 年かけて腹落ちした理由にも説明がつきます。

私が理解できなかったのではなく、理解できる位置まで技術が歩いてきたのかもしれません。まあ、これは自分に甘い解釈でしょうか。


まとめ

というわけで、ドメイン駆動開発の革新はアグリゲートだったんじゃないか? って話でした。

実践的な結論はシンプルです。**アグリゲートが要るかどうかで、作りを決める。**承認や状態遷移があって、守るべき整合が塊で存在するなら、包む。管理者ひとりのマスタ管理なら、包まない。テストの話は、その次に来ます。

そしてその正体は、20 年前に習ったカプセル化が「エンティティを操作する」概念を手に入れたものでした。新しい発明ではなく、古い概念の射程が伸びたわけですね。

結局、情報技術は数学と同じなのだと思います。ひとつの概念から、徐々に進化していくもの。

ということは、アグリゲートもまだ途中でしょう。この先、さらに深まった何かが出てくるはずです。それが何になるのかは分かりませんが、たぶんまた「昔どこかで聞いたやつだ」と思うのでしょうね。

みなさんも、いま使っている技術の歴史を辿ってみると面白いかもしれませんよ!

この記事をシェアする