ひがやすを技術ブログ

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

いまさらきけない「ドメインモデル」と「トランザクションスクリプト」

このネタは、私自身も何度も書いてきたけど、結局意味のある結論になったためしがありませんが、再度考え直してみたいと思います。
ドメインモデル」と「トランザクションスクリプト」をすごく簡単に説明すると、トランザクションスクリプトとは「アクションより起動される一連の手続き」、ドメインモデルとは「ドメイン内の名詞によって体系化されたモデル」です。
トランザクションスクリプト派は、「トランザクションスクリプトの方が書くのが簡単だし、業務アプリケーションにオブジェクト指向は、ほとんど必要ない」といいます。
それに対し、ドメインモデル派は、「ドメインモデルはオブジェクト指向を生かすことができるのでメンテナンス性が良い」と主張します。
ずっと平行線のままですね。
私は一番最初に「ユースケースと一対一にサービスクラスを設け、ビジネスロジックはサービスクラスに記述する」という主張をしてました。
記念すべき(?)第一回のダイコン時代の設計法でそういってますね。
http://d.hatena.ne.jp/higayasuo/20040606/1086495806

どうしてそうしたほうが良いかというと、ユーザの要件であるユースケースとその実装であるサービスクラスが一対一に結びついていたほうが、要件の変更が入ったときに、どのクラスを修正すればいいか一目瞭然なので、メンテナンス性が高いと考えたからです。
この方法は、トランザクションスクリプトではないと私は主張しました。手続き分割していくわけではなく、ユースケースごとに最も責任を持つクラスに分割しているし、DIによってオープンクローズの原則(OCP)もきちんと間持ているからです。
http://d.hatena.ne.jp/higayasuo/20050510
オープンクローズの原則の詳細はこちら。
http://www.morijp.com/masarl/homepage3.nifty.com/masarl/article/dp-ocp.html

でも、今から思うとこのやり方は、トランザクションスクリプトですね。PofEAAは注意深く読み直してみると、ユースケース(アクション)中心にオブジェクト(メソッド)を組み立てるのがトランザクションスクリプトで、ドメインオブジェクトを中心にオブジェクトを組み立てるのがドメインモデルだからです。

ただし、DIはOCPを満たしているのでオブジェクト指向の原則にあっているといえるでしょう。手続き型の持つ「上位のモジュールが詳細なモジュールに依存することで変更に弱くなるという欠点」を持っていません。DIを使ったトランザクションスクリプトは、手続き型の悪い設計ではなくオブジェクト指向の原則にのっとった筋の良い設計といえるのです。

じゃ、DIを使ったトランザクションスクリプトで何の問題もないかというとそんなことはありません。トランザクションスクリプトは、ユースケース別に設計されるので、複数のユースケースの間でロジックが重複しても気づかず、同じようなロジックが分散しやすいという問題があります。

ドメインモデルでは、ドメインオブジェクトごとに振る舞いが割り当てられるので、何か新しい振る舞いを追加するときは、既存のメソッドを調べたうえで、なかったら追加していくので、重複がおきにくいという利点があります。

PofEAAのドメインモデルは、ここまでしか説明されていません(オブジェクト指向の利点がフルに使えるとかはあるけど)。実際にドメインモデルで開発しようとすると、必ず、つまづくポイントがあります。

ドメインで扱う概念の中には、機能で閉じていて、もの(オブジェクト)として扱うのが不自然なものがある」

というポイントです。もの(エンティティ)として扱うことが不自然なものをきちんとエンティティに割り当てられるはずはないからです。

Domain Driven Designの中ではこのような機能は、サービスとして実装するのが良いとされています。
http://www.ogis-ri.co.jp/otc/hiroba/technical/DDDEssence/chap2.html#Services
ドメインの構成要素にはエンティティとサービスがあり(Value Objectはここでは省略)、ものとして扱うことが自然なものはエンティティに割り当て、ものとして扱うことが不自然なものはサービスに割り当てればいいのです。

サービスは基本的にエンティティと一対一で割り当てるのがルールが明快でいいと思います。そうしないと、似たような振る舞いが複数のサービスに分散する危険があるためです。これは、トランザクションスクリプトでロジックが重複しやすくなるのと同じ理由です。ドメインオブジェクト(エンティティ)に対するサービスに割り当てるというルールが明確なら、既存のサービス(やエンティティ)を調べることで重複を避けられるからです。

というわけで、最近私が思っているより良いビジネスロジックの実装方法は、DDDのサービスパターンを使ったドメインモデルです。


「エンティティをどのように抽出するの」という問題がまだ残っていますね。長くなったので、エントリを分けて書きます。