「モデル」を定義する

松岡幸一郎氏:では、モデルとは何でしょうか。いろんな人がいろんなことを言うんですね。DBA(データベース管理者)のような人だと「モデルとはDBのテーブルのこと」だと言ったり、サーバサードエンジニアの人だと「テーブルに対応したオブジェクトのこと」と言ったり、機械学習エンジニアの人は「数式のこと」をモデルと言ったりします。

モデルを作ることをモデリングと呼ぶわけですが、モデリングで価値を出していこうと言っているのに、モデルの定義が曖昧だと、みんなの議論が分かれてしまいます。ここで言葉の定義をしましょう。

ではどこの定義をもとにするのかというと、今回のDDDの文脈では、先ほどの「DDD Reference」のところを正解としましょう。いろんな捉え方があるので、「一番の正解はどれか」っていろんな意見を戦わせるよりも、いったんある「DDD Reference」を正にしたほうが話を進めやすいので、今回はここを正にしたいと思います。

上の英文がそれですが、正直、少しわかりづらい文章なので、ざっくり意訳をしたところで言うと、「モデルとは、問題解決のために、物事の特定の側面を抽象化したものである」と書かれています。「問題解決のために」というところと「抽象化」というところがポイントです。

ちなみに混乱しやすいものとして、ドメインモデルとデータモデルというのがあります。ドメインモデルというのは今回のソフトウェアで問題解決する対象のそのドメインの問題を解決するためのモデルです。データモデルというのはデータベースで何かを永続化するためのモデルです。これはデータベースのデータ永続化の効率化という問題を解決するためのモデルと捉えられます。

DDDでは、どちらも出てきます。ドメインモデルを作って、それを永続化するときにどうしようかというときにデータモデルが出てくる、そんな流れになっています。ですが、このあとのモデリングという話のときに、毎回「ドメインモデル」と言うとややこしいので、これから「モデル」と言ったときには、ドメインモデルのこととして進めたいと思います。

モデルと抽象化

「モデルとは問題解決のために物事の特定の側面を抽象化したものだ」と書かれているのですが、それでは抽象化とはいったい何でしょうか。その事例を紹介しながら解説したいと思います。

これは履歴書ですね。今の時代、履歴書を物理的に提出したことは、みなさん一度はあるのではないでしょうか。履歴書をソフトウェア化するときに、落とし込むためのいろんな要素があるわけです。ではどんな要素をデータベースに入れるかを考えると、思いつきやすいところで言うと、名前とか経歴とか志望理由がパッと思いつきますね。写真なんかも実際貼ったりしますよね。

でも、実際の物理的な履歴書を思い返してみると、こういう要素もあります。筆跡だとか筆圧だとか、紙の紙質だとか折れ具合というもの……。それから履歴書のメーカーとか値段とか、書いたときの気持ちも実際の履歴書の要素としてはありますよね。でも、そのようなすべての現実世界の要素をソフトウェアに落とし込むことは実際できないことがわかったと思います。

しかも、それは落とし込む必要もないわけです。必要な要素はモデルに取り込みたいですし、不要な要素はモデルに取り込みたくない。こうした選択、どれを取り込んでどれを取り込まないか選択することが抽象化です。

よいモデルとは何か

では、よいモデルとは何でしょうか? これ実際にやると(オフラインでイベントをやると)呼びかけたりするんですが、今日はそれができないので進んでしまいます。こういうふうに言ってくる。こういうふうなことが出てきたりするんですね。

わかりやすいモデルだとか現実をうまく反映しているとか拡張性のあるモデルという答えが出てきたりもするのですが、この文脈からすると、問題解決ができるモデルがよいモデルであると言えるかなと思います。なぜかというと、問題解決するために作るのがモデルだからです。それができるモデルがよいモデルだと言えると思います。

よくないモデルとは何かというと、問題解決ができないモデルです。現実に即していないとかやりたいことができないものです。

よくないモデルの実例

といっても、なかなか実際どういうものかイメージがつかめないと思うので、事例を挙げたいと思います。

これは採用管理のドメイン、採用管理を効率化したいというドメインだとします。使う人は、人事とか採用担当者とかでしょうか。人事や採用の人が求人・応募してきた人を管理するモデルとして、求人があって、応募してきた候補者の人がいて、間に採用進捗というものがあるモデルを作りました。採用進捗は、書類選考があって、次に一次・二次・最終・内定というステップを踏むというふうにしました。

このモデルを実際に作ってみて、リリースしてみて運用に回してみたらどうなったかというと、「実は、書類選考の前に面談があるのになぁ」とか「面接15時16時もあるのに入力できないなぁ」とか「求人に関係ない応募も許可したいのにつなげないなぁ」とか困ったことがあって……。

こういう困ったことがあるけれども運用しなければならないと、どうなるかというと、運用でカバーとなってしまいます。なんかすごく技術の敗北感がありますよね。これが、モデルがよくなくて問題解決ができない事例です。

運用でカバーできればまだマシで、売れなければプロダクト自体がなくなってしまいます。社内プロダクトであればカバーが利きますが、SaaSのサービスで、実際にユーザー体験でお客さんが離脱したみたいなことになると、もう売れなければプロダクト自体がなくなってしまうので、モデルの質は、かなりプロダクト自体の命にも関わるような重大なものであることがわかっていただけたかと思います。

モデルは最初から完成しない

では、ちょっと考えてみてください。もしもこのソフトウェアがDDDの実装パターンを的確に使っていて、コードの可読性・拡張性が完璧で、テストも完璧でバグがまったく出ないのであれば、高い価値を生み出せるでしょうか?

そうはならないですよね。モデルがイケてないと、その後の実装がどんなにすばらしくてもいいソフトウェアにはならないことがわかっていただけたのではないでしょうか。

では、よいモデルを作るにはどうすればよいでしょうか? 簡単です。ドメインに詳しい人から知識を得るのです。採用の管理の話であれば、採用管理を担当している人に話を聞きに行かずに作っては、やはりいいものにはなりません。想像のとおりですよね。

そして運用した知見をモデルにフィードバックして改善するのも大事なことです。というのも、やはりドメインに詳しい人事担当者はプロダクトづくりのエキスパートというわけではないので、実際に作ってみたらこうだったなという発見があります。

なので、これはDDDとしてとても重要なスタンスなのですが、モデルは最初から完成しない、改善していくものになります。

モデルとコードの乖離

モデルは最初から完成していなくて改善していくものだとして、モデルとコードがこのような状況だったとします。

スカウトメールみたいなものを候補者の人に送りますというモデルがあったとして、これをコードに落としましょう。技術的にプーリングするのでScoutPoolというのがあって、メッセージとスレッドというものがありますよね。ということで、こんな実装にするとします。

これってつまりモデルとコードの形が離れているということですよね。コードとモデルが乖離してしまうとどうなるかというと、左側のモデルにちょっと修正、新しい発見が得られたのでモデルを更新しました。それでスカウトメールというモデルにちょっと修正を加えました。ではそれはコードのどこに行くのかを見てみると、パッとわからないですよね。

こうして本当にコードに正しく反映しているのかが、ひと目でわからなくなっていきます。複雑になっていくほど、どんどんわからなくなっていきますよね。

さらに、モデルからコードを理解しにくくなる。逆もしかりで、コードからモデルを理解することもなかなか難しくなっていきます。

最初にモデリングをした人がコードに落としたとしても、運用していくと、最初にモデリングした人ではない人もコードの開発に参加することが基本的に大半だと思います。ではモデリングしていない人がコードを見たときに、この右側のコードを見て左側を想像できるかというと、なかなか難しいですよね。そうすると、理解できないものを改善することもなかなか難しいです。

この2つの問題がどうつながるかというと、モデルの更新をコードに正しく反映しているかわからないということは、意図していない、本来モデルとしてやりたかったこととは違う実装になってしまう。これはつまりバグと言っていいと思うのですが、バグを生みやすくなってしまう。

コードからモデルを理解しにくくなると、モデルの理解ができない。そしてモデルで問題解決しようとするドメインに対する理解もできない。そうするとモデル自体が改善されなくなってしまって、最終的にソフトウェアの価値を高めにくくなってしまいます。

オブジェクト指向の手法を使って乖離を回避する

では、それに対してどういうことをすればよいのでしょうか? コードとモデルが乖離すると問題が発生するので、「どうすればいいの?」という現実はさておき、理想で言えば、モデルをそのまま表現したようなコードになると望ましいと言えます。

そこで「オブジェクト指向の手法を使って、さきほどのモデルをオブジェクトを使ってできるだけ同じような見た目・形になるような実装をしよう」というのが1つのアプローチになります。

また、もう1つ「モデルは継続的に改善していきたい」ということに関しては、コードを継続的に反映しないといけない。それってつまりコードとしては、実はとても高いハードルを課しているんです。それだけの頻繁な変更に耐えうるだけの拡張性の高い設計が必要になります。

これはソフトウェアとしては非常に高い要求なのですが、それならこれをどういうふうに実装したらいいのかというのを、DDDを先に研究してきたいろんな人がいろいろと研究して出た結果のベストプラクティスというようなものが、EntityとかRepositoryというようなデザインパターンのようなものになります。

EntityやRepositoryは、それ自体がDDDというわけではありません。モデリング結果をコードに落としてソフトウェアの価値を高めていきたいと言ったときに、「こうするとそれがやりやすいんじゃない?」というような位置づけなのがEntityとかRepositoryと思ってもらえればと思います。

今コメントをいただきましたが、関数型にも適用可能かというところでいうと、ちょっと私、関数型でそこまできちんとやったことはないんですが、意図としてはこのようなモデルとコードが一致していて理解ができればよいと思うので、それが関数型の形でできるのであればやりやすいし、できなければ向いていないこともあるのかなと思います。

軽量DDDとは

ちなみに軽量DDDというのも、よく話題に出るんですが、これは何かというと、さきほどのEntityやRepositoryというDDDの実装パターンだけを取り入れてみることだと、『実践DDD』の本の中で紹介されていました。

「じゃあ、これって価値がないの?」「ダメなんですか?」と、よく話題になるんですが、さきほど書いたとおり、非常に頻繁な設計をきちんと反映できる拡張性の高いベストプラクティスなので、実はその拡張性の高いものを取り入れるだけでも十分価値があると言えると思っています。

モデリングしたいと言っていても、そもそもコードがぐちゃぐちゃ過ぎてそこまで到達できない。コードの品質自体がボトルネックになってしまうことは非常にあると思います。そこでこうした実装パターンを使ってコード品質を上げることで、ようやくモデリングする余地が生まれるケースもあるし、そういうことと考えることもできると思います。

ですから、軽量DDDに変えることは何の問題もないので、どんどんそこからやって、モデリングにも踏み込んでいくというふうにできればいいかなと思っています。

モデルがイケていることが大事

ここまでの全体像を図に表してみました。一番やりたいことは、左に書いてある、問題をドメイン……ソフトウェアを適用する現実世界の問題を解決したいということです。それに対してのアプローチとして、ドメインの問題を解決するためのモデルを作ります。

アプローチ1としては、ドメインについての理解を深めてモデルを継続的に改善していく。このとき、ドメインに詳しい人の知識を取り入れたり、運用した知見を取り入れたりして継続的に改善していく。そしてもう1つが、モデルをソフトウェアに反映していくそのときに、ソフトウェアを反映しやすいような拡張性の高い実装をしていくというのがアプローチ2です。

先ほどの繰り返しになりますが、モデルがイケていないと実装がどんなにすばらしくてもいいソフトウェアにはならない。さらに、モデルがどんなによくてもソフトウェアに落とせないと実際に価値は生まれないので、それができるような拡張性の高い設計・実装をしましょうということです。

ではこのあと、実際にモデリングの事例とソフトウェアのコードに落とすところの実例を紹介していきたいと思います。