ひがやすを技術ブログ

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

ActionとServiceの統合?

これまで、Actionはプレゼンテーション層のコントローラ(コマンド)であり、Serviceはビジネスロジック層のコントローラ(ファザード)という話をしてきました。ActionはプレゼンテーションモデルをServiceに渡し、ServiceはDxoを使って、プレゼンテーションモデルをドメインモデルに変換し、ドメインモデルを実行するという流れです。Serviceのメソッドにトランザクション境界があります。
Dxoは、ビジネスロジック層にあった方がいいというのが、http://d.hatena.ne.jp/higayasuo/20050824#1124874756で書いた話です。
これは、人のスキルや役割分担という意味でも、アーキテクチャーとしてもきれいにできていると思うのですが、若干気がかりな点があります。ActionはServiceに処理を委譲しているだけであり、ほとんど働いてない点です。データアクセスしかないビジネスロジックの場合、Serviceもほとんど働いてません。
まこたんから、「DxoをActionに持っていって、トランザクション境界をActionのメソッドに持っていったら良いじゃん」という提案がありました。
これまでは、トランザクションは、ビジネスロジック層で処理すべしという固定観念があったので、ありえない考えだと思っていたのですが、なぜありえないかというと気持ち悪いという意外に急に理由を思いつけません。私の固定観念だったのかもしれません。
トランザクション境界をActionのメソッドに持っていくと、ActionとServiceを統合することが可能です。すると、長年の懸念であったデータアクセスしかしないビジネスロジックの場合に単にDaoに処理を委譲するだけのビジネスロジック層のクラスを造らなければならない問題も解決します。
Actionをプレゼンテーション層とビジネスロジック層のglue(Controler)として使うわけです。
これに対応したアーキテクチャーも考えてみます。

PB(Presentation Businesslogic)レイヤアーキテクチャ

Actionをプレゼンテーション層とビジネスロジック層のglueとして使う場合、レイヤとしては、プレゼンテーション層とビジネスロジック層の2層構造になります。データアクセス層はDaoとしてビジネスロジック層に吸収します。ActionからDaoを呼び出す場合もあるためです。ビジネスロジックは、ドメインロジック、データアクセスロジック(Dao)、アプリケーションロジックで構成されると考えてもそんなに違和感無いんじゃないかと思います。
その場合の各層は次のようなクラスで構成されます。

  • プレゼンテーション層
    • View
      • JSP、HTMLなど
    • プレゼンテーションロジック
      • ページングなどプレゼンテーションにかかわるロジック
      • ActionにDIされる
    • プレゼンテーションモデル
    • Action
    • Dxo
      • ActionにDIされる
  • ビジネスロジック
    • アプリケーションロジック
      • ActionにDIされる
      • アプリケーションロジックがアプリケーションロジックを利用している場合アプリケーションロジックにDIされる
    • (ドメインロジック)
      • リッチドメインモデルの場合はドメインモデルに統合
      • シンドメインモデルの場合はActionにDIされる
      • アプリケーションロジックがドメインロジックを利用している場合アプリケーションロジックにDIされる
    • ドメインモデル
    • Dao
      • データアクセスロジック
      • ActionにDIされる



リッチドメインモデルの検索系の場合、次のような処理が行われます。

  1. Actionがプレゼンテーションモデル(検索条件)を受け取る。
  2. ActionはDaoを使って、ドメインモデルを取得する。
  3. ActionはDxoを使って、ドメインモデルをプレゼンテーションモデルに変換する。



リッチドメインモデルの更新系の場合、ビジネスロジック層では次のような処理が行われます。

  1. Actionがプレゼンテーションモデルを受け取る。
  2. ActionはDxoを使って、プレゼンテーションモデルをドメインモデルに変換する。
  3. ActionはDaoを使って、ドメインモデルを永続コンテキストに登録する
  4. Actionはドメインモデルのドメインロジックを呼び出す
  5. 必要ならActionはアプリケーションロジックを呼び出す

Actionのメソッドにはトランザクション用のAOPが設定されているので、メソッドが開始するときにトランザクションが開始され、メソッドが終了するときに、自動的にO/R MapperによりSQL文が発行され、コミットされます。


シンドメインモデルの検索系の場合、リッチドメインモデルの場合と同じです。


シンドメインモデルの更新系の場合、リッチドメインモデルとの違いは、4の部分で、ドメインモデルを引数にしてドメインロジックを呼ぶ部分だけです。


Actionがアプリケーションロジック、ドメインロジック、Daoを直接呼び出すようにした場合、ビジネスロジックの仕様が変わるとActionにも影響が及んでしまいますが、その仕様変更は、アプリケーションロジック、ドメインロジック、Daoにカプセル化されていますから、特に問題なさげですね。
Dxoはプレゼンテーション層に所属していながら、実際にコードを書く人は業務に精通しているビジネスロジック層の人になると思われますが、これはしょうがないですね。

「仕様変更の影響を局所化する」ことが良い設計

ここでの仕様変更とは、仕様追加も含むこととします。そうすると、アジャイルだとかウォーターフォールだとかに関係なく、開発のかなりの部分は仕様変更による対応で占められることになります。仕様変更に対し、いかに容易に対応するのかがポイントになってくるわけです。
平鍋さんのEoM = EoT + EoCシリーズもぜひお読みください。
http://blogs.itmedia.co.jp/hiranabe/2005/08/post_e66c.html
http://blogs.itmedia.co.jp/hiranabe/2005/08/_eocease_of_cha_602c.html
http://blogs.itmedia.co.jp/hiranabe/2005/08/eom__ease_of_ma_8db3.html
歴史的な流れで言うと、グローバル変数などを多用したCOBOLでの開発では、仕様変更が起きたときに、影響範囲が思いのほか大きくなってしまうという問題点がありました。昔は、比較的開発期間を長く取ることができたので、それでも何とか対応できていたのですが、開発期間の短縮が叫ばれるようになるとそれでは対応ができなくなってきたのです。
そこで仕様変更が起きたときでも、その影響範囲をできるだけ局所化する技術として登場したのが、OOPの言語です。
関連するデータと振る舞いをカプセル化するという考えも、まさに「仕様変更の影響を局所化する」ことから生まれたものです(と思う)。「仕様変更の影響を局所化する」という目的を達成するための手段として、「関連するデータと振る舞いをカプセル化する」方法があるわけです。最近(結構昔から)は、「関連するデータと振る舞いをカプセル化する」という本来は手段だったことが目的になってしまっている人がいるみたいで、残念に思うこともあります。
それでは、「仕様変更の影響を局所化する」にはどうすれば良いのでしょうか。
それには、クラスの凝集度を上げ、クラス間の結合度を下げることが重要です。
赤坂さんの「設計におけるオブジェクトの責務分配に有効なものさし」もぜひお読みください。
http://www.ogis-ri.co.jp/otc/hiroba/technical/Cohesion_Coupling/Cohesion_Coupling.html
凝集度を上げるためには、関連する機能を1つにまとめることが重要です。逆に関連の無い機能をいっしょにした場合、凝集度は下がることになります。関連する機能が1つにまとまっていると仕様変更があった場合に、その特定のクラスだけを修正すればよくなるので、影響を局所化できます。
結合度を下げるためには、お互いに相手のことをできるだけ知らなくても良いようにします。インターフェースで会話をするようにすると結合度を下げることができます。実装クラス同士がお互いに知り合いなのは、できるだけ避けるべきです。1つの実装クラスの変更が変更の連鎖を引き起こす場合があるためです。変更が連鎖しなければ、影響を局所化できます。
クラスの凝集度を上げ、クラス間の結合度を下げることがOOの重要なポイントだといえます。

Viewでドメインモデルを直接扱ってはいけない

http://d.hatena.ne.jp/bakock/20050824#p1

画面にはドメインオブジェクトを渡す派は、本来のOOP的な考えにマッチしてるように思える。ドメインのモデルをどう整形して表示してるか、ソースに直接書いてあって、トレーサビリティが高い。凝集度が高いといってもいいかもしれない。

Viewでドメインモデルを直接扱うというのは、Viewにドメインモデルの知識(ある意味ドメインロジック)があることになり凝集度は低くなります。Viewは本来やらなければいけないプレゼンテーションの機能以外にドメインの知識ももっているためです。
それでは、Dxoを使ってプレゼンテーションモデルとドメインモデルの変更を行うようにしたとしましょう。Viewはプレゼンテーションに徹していて凝集度は高くなります。Dxoドメインモデルの知識を持つことになりますが、Dxoは、プレゼンテーションモデルとドメインモデルの知識を持って変換することが本来の役割ですから、Dxoの凝集度も高くなります。
Viewでドメインモデルを直接扱うのは、凝集度が下がり悪い設計になるということです。もちろんOOP的ではないと思います。

プログラミングの方法から見ると、画面にはドメインオブジェクトを渡す派のほうがpull的というか、画面がデータを欲しがるときにとってくるという感じ。プレゼンテーションモデルをビジネスロジック層で変換する派は、バッチ処理的にプレゼンテーションモデルを作って「おら、表示しろ」って感じ。前者の方がOOPチックでスマート、後者のほうは汎用機出身の人なんかにはわかりやすいかも。

プログラミングの方法から見ても、プレゼンテーションモデルを使えば、Viewは常に必要最小限の事を知っていれば良いので、良い設計ということがいえるのではないかと思います。
ドメインモデルは、プレゼンテーションモデルに変換してからViewで使うべきだということです。ドメインモデルとプレゼンテーションモデルが一致していれば、ドメインモデルを直接使ってももちろん問題ありませんが、それは非常にまれなケースだと思います。例えば、汎用コードテーブルなどを使ってコードと名称を管理するのは良くあるケースだと思いますが、これだけでも既にドメインモデルを直接使うことはできなくなります。