質疑応答 ドメインモデルパターンはドメイン騒動設計と同義か?

大嶋勇樹氏:ということで、ここまでビジネスロジックの実装について話してきました。ここからは最後のステップとして、「Controllerに全部書く」からどうやってステップアップするかを話していこうと思います。

ここまでで質問があれば、ぜひQ&Aにもらえれば回答します。せっかくなので、このタイミングで「ドメインモデルパターンはドメイン駆動設計と同義ですか?」(という質問)に回答しておこうと思います。

ドメイン駆動設計はドメインモデルパターンを使いますが、ドメインモデルパターンを使ったからといってドメイン駆動設計ではないという関係だと私は認識しています。こういう質問が来た時のために、私の横にホワイトボードを用意していたので、そちらを使って解説できればと思います。

せっかくなので、ドメイン駆動設計と今日話しているドメインモデルパターンの関係を話そうと思います。

(ホワイトボードに書きながら)これも人これも理解の仕方が違うところが大きいですが、ドメイン駆動設計は大きく要素が2つあります。1個は戦略的設計というもので、もう1個は戦術的設計です。DDDの本を読んでいても、こういう言葉、こういう分類みたいな感じでいろいろ話されます。

この中で、今日はレイヤー化で3層にするとか、レイヤードアーキテクチャで4層にするという話をしましたが、そういう「レイヤー化はどうする」みたいな話も戦術的設計の一部として出てきます。あとは、ドメインモデルを具体的にどう実装するべきかみたいな話も出てきます。

ただ、ドメインモデルパターンを使えばDDDということではないし、DDDはドメインモデルパターンを使うことを想定した設計のプラクティスではありますが、イコールではないというところですね。

より大事なのは、戦術的設計、実装方式に注目してDDDと呼んでいる例がけっこうあります。戦術的設計だけやっているのは軽量DDDと言われて、場合によってはアンチパターンとも言われます。

個人的には、それでもやってもいいと思うし、別にそれ自体(だけで)もやる価値はあると思うのですが、戦術的設計だけをやるのはあまり良くないよと言われることもあります。

DDDの中で私の理解がすごく大事なのは、戦術的設計も大事ですが、それ以上に戦略的設計と言われるところです。1個はDDDの思想というか、考え方みたいなものです。

DDDはドメイン駆動設計の略ですが、ユビキタス言語を使ってドメインエキスパートと話してドメインモデルを整理していこうとか、そういった「実装上どうだ」だけではなくて、ドメインエキスパートという領域にすごく詳しい人とユビキタス言語、共通言語を使ってちゃんと認識合わせしましょうとか。

その認識合わせをして出来上がったモデルを、そのままコードに落としましょう。それができるように、ちゃんとドメインモデルを隔離したレイヤー化をしましょうという話です。

特にDDDっぽくちゃんとやってるとなると、コードの実装だけじゃなくて、「ドメインエキスパートとの話をちゃんと聞いていますか?」とか「ユビキタス言語と言いますが、そういう言葉の使い方をしていますか?」とか。そちらのほうがむしろ主眼かなと思っています。

あとはサブドメインみたいな言葉や境界づけられたコンテキストが出てきますが、システムでいえばシステムのどこで分割するべきかにも関係してくるような要素も出てきます。

このあたりを全体的に含めてDDDだと私は思っているというか、そういう理解の人もある程度いるはずです。なので、ドメインモデルパターンをやっていたらDDDではなくて、DDDの重要な要素の1つにすぎないという感じです。

逆に、戦術的設計だけやっているのはDDD的にはあまり良くないとされることもある関係です、ということで質問に回答しました。

ノンフレームワーク・軽量フレームワークを使用している場合のステップアップ方法

ちょっと画面共有に戻って最後の話をしていければと思います。質問があれば、どんどんQ&Aにもらえれば、適宜回答します。

ということで、「Controllerに全部書く」からのステップアップを話していこうと思います。

ここまでいろいろ実装の仕方を見てきましたが、具体的に今あるコードを「自分がわりとControllerにいろいろ書いているな」という状態から、どうやってステップアップしていけばいいのかみたいなところを話していこうと思います。

例えばノンフレームワーク、フレームワークを使わなかったり、Node.jsのExpressなどの軽量なフレームワークを使ったりしていて、ControllerにSQLまで書かれている例を考えてみようと思います。最近はORマッパーを使うので、SQLが書かれていることはあまりないかもしれません。

(スライドを示して)例えばControllerにSQLが書かれている(場合)というのは、こんな構成になるわけですね。プレゼンテーション層、ビジネスロジック層、データアクセス層にまたがってControllerがあります。

利用者とやりとりするのもControllerの役割だし、ユースケースの実現とドメインロジックというビジネスロジックのいろいろな要素がControllerの役割だし、データベースでやりとりするSQLも書かれています。

一応データベースと一致する、データベースのテーブルと合わせたデータの入れ物クラス。Entityと言うこともありますが、なんとかRecordみたいなクラスやEntityと言うこともありますがあとUIは、テンプレートエンジンを使っていて、一応分離されている構成も見なくはないですね。この場合、あまり意識してやっているわけではないと思いますが、こういうコードも見かけることはあり得ます。

ここからどう整理していくかはいろいろあるとは思いますが、例えばまずデータアクセス用のクラスを設けてみます。一例として、データベースと1対1で対応するGatewayみたいなものや、なんとかGateway、Player Gatewayみたいなものを設けてみてもいいと思います。

でもこれはControllerの役割が多すぎるんですね。利用者とのやりとりもそうだし、ユースケース、ドメインロジックもそう。

(このように)Controllerの役割が多すぎるので、例えばServiceクラスを設けてみます。プレゼンテーション層とビジネスロジック層をしっかり分離するわけですね。Serviceクラスには、ユースケースとドメインロジックを持たせます。

こうなってくると一応整理されている、よく見るコードになってきているイメージです。この間にDTOとかなにかは多少いるかもしれませんが、ざっくり、ControllerとServiceとデータアクセス用のなんとかGateway、Gatewayの名前はいろいろありますが、こういう構成はわりと見かけます。

ただ、この構成はServiceの役割が多すぎます。そして、複数サービスで似たドメインロジックを実装したい時に、コードが重複しやすいです。似たというか、まったく同じドメインロジックを実装したい時に、重複すべきではないドメインロジックがサービス間で重複しやすい構成だったりします。

そこでドメインロジックをModelに持たせると、Serviceの見通しが良くなりやすいです。Serviceがユースケースもドメインロジックも持っていると、Serviceのメソッドが100行とかにもぜんぜんなるし、普通に30行、50行とかになって見通しが悪くなっていきます。

結局はドメインロジックの設計次第なところもありますが、ドメインロジックをドメインモデルにしっかり持たせて、そこをしっかり整理していけば、ある程度コードも見通しが良くなっていくかなと思います。

(スライドを示して)さらに加えて、これはビジネスロジック層と書いてありますが、ドメイン層をアプリケーション層、ドメイン層と分けたり、Repositoryパターンを導入したりということも考えられるとは思います。そうすると、まさに途中で説明したようなアーキテクチャになっていくイメージですね。

ポイントは「クラスごとの役割分担」

ここでのポイントは、クラスごとの役割分担だと思います。難しい表現はいろいろありますが、役割分担はけっこうわかりやすい言い方かなと個人的に思っていて、クラスごとにどんな役割を持たせるかがすごく大事だと思います。

これでは1個1個のクラスの役割が、「このクラスの役割は何ですか?」「このクラスは、テーブルと一致するデータの入れ物です」「このクラスの役割は、ユースケースです」と、一言で説明したりがすごくしやすいと思います。

こういう役割分担がすごく重要だと思います。ここに出している役割はあくまで例であって、例えばこれ以外にも表示用の変換が必要な場合がよくあると思います。

その表示用の変換は、どこかの既存のクラスに入れてしまうこともあるし、「Presenterというクラスを作って、そこに書きましょう」とすることもよくあります。こんなやり方が1つあると思います。

(次回に続く)