ひがやすを技術ブログ

電通国際情報サービスのプログラマ

3つのモデル - どのモデルを中心にするのか

モデルは、その使われる場所によって、プレゼンテーションモデル、ドメインモデル、ERモデル(RDBMSの場合)に分かれます。プレゼンテーションモデルはプレゼンテーション層で使われ、ドメインモデルはビジネスロジック層で使われ、ERモデルはリソース層で主に使われることになります。
どのモデルを開発の中心に置くのかで、開発のスタイルが分かれてきます。

プレゼンテーションモデル中心派

プレゼンテーションモデル中心派は、ドメインモデルを使うことなく、ERモデルをプレゼンテーションモデルに直接変換し、ビジネスロジックにプレゼンテーションモデルを渡すことで処理を実行します。ビジネスロジックとプレゼンテーションモデルはクラスとして分離されています。
くーす、TableModule(.NET的?)がこの方式です。最もシンプルであまりビジネスロジックが無いシステムに向いています。スクリプト言語を使う場合も向いているでしょう。
プレゼンテーションモデル中心派の最もシンプルな実装は、Action、Service、Dao、Dtoで構成する方法です。画面にデータを表示するときは、ActionがServiceに処理を依頼し、ServiceはDaoに処理を依頼してDtoを取得し、それをActionに返します。画面から受け取ったデータを更新する場合は、ActionがServiceにDtoを渡し、Serviceは何らかのビジネスロジックを実行した後でDaoにDtoを渡して永続化します。ビジネスロジックはServiceで実装することになります。役割分担が明確で、恐らく誰がやっても失敗しないのがこの方法です。
この方法の問題点は、プレゼンテーション(画面)ごとにServiceクラスが割り当てられ、Serviceクラスにビジネスロジックが実装されることになるため、複数の画面で同じようなロジックがあった場合に同じようなロジックが分散(重複)してしまうことです。これがファウラーが言っているトランザクションスクリプトの問題点です。
この問題への解決策として、くーすでは、ビジネスロジックを「xxxをyyyする」という単位で分解し、xxxの単位でクラスにまとめます。これにより、ロジックが分散するのを防ぎ、凝集度を上げます。xxxは基本的にドメインオブジェクトの名前と一致するはずなので、ドメインモデルと凝集度は基本的に一緒です。この方式のドメインモデルに対する利点は、変化の速い部分(ビジネスロジック:ホットスポット)とあまり変化しない部分(データ構造)をきちんと分離できているため、システムが安定することです。
これが、くーすの理論ですが、実はネック(難しい)となる部分があります。複数の画面から同じビジネスロジック(xxxをyyyする)を呼び出したときに、そこで使っているデータ(Dto)は異なる場合がほとんどなので、共通部を1つのクラスに抽出するところです。この共通部分のクラスは実は、ドメインオブジェクトとかなり近いものになるはずで、なら最初からドメインモデルを中心にした方がいいじゃんというのが、goyaのスタート時点になります。

ドメインモデル中心派

ドメインモデル中心派の中でもプレゼンテーションモデルを使う派と使わないでドメインモデルをそのまま使う派に分かれます。xxx派と言うのは一概にどれが良いとはいえないことが多いのですが、この件に関しては、プレゼンテーションモデルを使った方がいいと断言しておきます。
理由は、いろいろあります。

  • プレゼンテーション層とビジネスロジック層ではモデルに対する要求が違うのに、それを1つのモデルで対応しようとするのは無理が生じる。役割持ちすぎ。
  • プレゼンテーションモデルを使って、ドメインモデルと切り離しておけば、ドメインモデルのリファクタリングをきちんと行えるようになる。ドメインモデルがプレゼンテーション層に公開されているとドメインモデルのリファクタリングがうまくいかない。
  • プレゼンテーション層でドメインモデルの構造を理解して関連をたどるのは、プレゼンテーション層にビジネスロジックが入り込むことになる。
  • ドメインモデルに無い項目をプレゼンテーション層で表示しようとしたとき、ドメインモデルにプレゼンテーション用の項目を追加するという間違いを犯しやすくなる。

もっとあるけどこれくらいでも十分でしょう。
ドメインモデルを使うときに、よく問題になるのが、ERモデルとのインピーダンスミスマッチですが、テーブルのプライマリキーをシステムが自動的に採番するidにしてxxx番号のような意味的なキーはプライマリキーにしない、ManyToOne, OneToMany, OneToMany, ManyToManyにあわせて、それぞれFKや対照テーブルを作成するという感じで、ERモデリングを行えば、ドメインモデルとERモデルは一対一でマッピングできます。後、必要ならドメインモデルに継承や埋め込みオブジェクトの設定をするだけです。いまどきのO/R Mapperは賢いですからこの辺は難無くやってもらえます。これまで、インピーダンスミスマッチが起きていたのは、もしかするとドメインモデルがプレゼンテーション層に引っ張られていたことが原因である可能性もあります。
ドメインモデルと親和性の高いERモデリングの話はまた別途します。
プレゼンテーションモデルとドメインモデルを使う場合に必要になるのが相互のモデルを変換することです。この変換を行うのがDxoであり、S2Dxoというフレームワークを用意してかなりの部分を自動化することを狙っています。コストがかかるといわれることもあるけど、1,2時間あれば素で書いても実装できると思うのは「わたしだけ?」。
ドメインロジックの実装の仕方は、ドメインモデルにドメインロジックを持たせるリッチドメインモデルとドメインモデルとドメインロジックを分離するシンドメインモデルに分かれます。
どちらの方法でも、ユーザの要求を「見える化」する方法は同じです。http://www.xpjug.org/event/20050903matsuri/UCD.pdfの業務手順書(最新では作業手順書)を使って仕様を「見える化」します。
結局、xxxをyyyする形式に分解できるので、リッチドメインモデルの場合は、xxxに一致するドメインオブジェクトに対してメソッド(yyy)を割り当てます。
シンドメインモデルの場合には、「xxxをyyyする」ごとに1つのクラスにマッピングする方法(メソッドオブジェクト方式)とxxxごとに1つのクラス(xxxLogic)にまとめる方法(集約方式)があります。
リッチドメインモデルのロジックを呼び出す場合には、ドメインオブジェクトのメソッドを呼び出します。シンドメインモデルのロジックを呼び出す場合には、ドメインロジックオブジェクトのメソッドの引数にドメインオブジェクトを渡します。
リッチドメインモデルのメリットは、既存のオブジェクト指向になれている人にとって分かりやすいことです。
デメリットとしては、

  • DIコンテナで管理されるような非永続オブジェクトをドメインオブジェクトに現状ではDIできないこと。なぜなら、ドメインオブジェクトの生成は、DIコンテナではなく、O/R Mapperで行われるためです。
  • 複数のユーザの要求(ユースケース)をまたがるようなドメインオブジェクトは要求単位での開発が難しいという問題もあります。
  • 変化の速い部分(ビジネスロジック:ホットスポット)とあまり変化しない部分(データ構造)をきちんと分離できていないので仕様変更の多いシステムには向かないという問題もあります。

シンドメインモデルの集約形式のメリットは、

  • DIコンテナとの相性が良いこと。なぜなら、オブジェクトがDIコンテナで管理されますから。
  • 変化の速い部分(ビジネスロジック:ホットスポット)とあまり変化しない部分(データ構造)をきちんと分離できていること。

デメリットとして、複数のユーザの要求(ユースケース)をまたがるようなドメインロジッククラスは要求単位での開発が難しいという問題があります。
シンドメインモデルのメソッドオブジェクト形式のメリットは、

  • DIコンテナとの相性が良いこと。
  • 複数のユーザの要求をまたがるクラスが無いので、要求単位での開発が容易であること。
  • ロジックをピンポイントで置き換えることが容易であること。
  • 変化の速い部分(ビジネスロジック:ホットスポット)とあまり変化しない部分(データ構造)をきちんと分離できていること。

デメリットは、クラスが細分化されるため、凝集度が低く見えることです。実は凝集度が低いことの問題は、関連の無いクラスが1つのクラスにまとめられるときに起こるので、別に問題じゃないんですけどね。ユーザにとって意味のある手順の最小単位で1つのクラスにまとまっているので、細分化が行き過ぎているわけではありません。
私のお勧めは、シンドメインモデルのメソッドオブジェクト形式ですが、3つの内どれを選ぶのかは好みでいいと考えています。