Hatena::ブログ(Diary)

yvsu pron. yas このページをアンテナに追加 RSSフィード

2008-02-07

DaoとServiceっている?

今まで

action service dao

って言うレイヤで組んでたので

(いつもserviceとdaoは空っぽに近い)

actionだけで書く、ってのがちょっぴり違和感

tpircs’s diary インサイド・アウト ?刺激と反応の隙間

最初にDaoがいるかって話なんですが、あってもいいと思うけど、今後は使われなくなっていくだろうと思っています。

S2JDBCも当初は、Daoの皮をかぶせるつもりだったんですよ。でも、自分で実際に使ってみると、ジョインの仕方が違うだけなのに、メソッドを変えることに違和感がでてくる。または、めんどくさく感じてくる。

S2JDBCの流れるようなインターフェースは適度に抽象化されていて可読性が高いので、Daoのメソッドを呼び出すよりも、何をやっているのかがわかりやすいのです。Daoのメソッド名を適切につければ、Daoのメソッドのシグニチャだけでも伝わると思いますが、引数やジョインのバリエーションが増えてくると適切にメソッド名をつけるのが非常に難しくなります。

Daoパターンの一番のメリットは、データアクセスコードが1つにまとまっているので、スキーマの変更があったときに、影響範囲を調べるのがやりやすいということがあるでしょう。

たぶん、Daoを使わないときに一番気になるのは、この問題だと思いますが、SQLファイルはテーブルごとにまとめることを推奨しているので、SQLファイルを書く分には問題ありません。

SQL自動生成する部分は、S2JDBC自動生成しているんだから、いろんなところから呼び出されていても特に問題はないと考えています。スキーマの変更があった場合も、エンティティを修正すれば良く、個別に呼び出しているコードを修正する必要はほとんどないでしょう。修正する必要があるケースは、Daoを使っていても呼び出し側を修正しなければいけないことがほとんどではないでしょうか。

次にサービスが必要かどうかですが、あってもいいと思うけど、多くのケースではちょっと重い(過剰)かなと思います。

SAStrutsのサンプル(employee)を書くときに、最初は、ActionとServiceは分けていたんです。で、どうだったかって言うと、ActionはS2BeanUtilsを呼び出すコードだけがあり、Serviceには、S2JDBCを呼び出すコードだけしかない。

だったら、1つユースケースに閉じていることは、すべてActionに記述し、複数のユースケースで使われるものをServiceLogicに分割するのがシンプルでわかりやすいのではないかと、そう思ったわけです。

これが、今の私の考えです。

追記:Serviceだとトランザクション境界のServiceと混同しやすいので、Logicがいいんじゃないのという小林さんのアドバイスがありました。確かに、そうですね。Action以外のロジックLogicに記述するというほうがわかりやすそうです。

追記2:コメントへの回答ですが、ユーティリティは、staticメソッドで構成されるやつは、utilのパッケージ。ActionにDIするやつは、logicパッケージでいいと思います。どっちでもないユーティリティもutilパッケージですね。

Entityについては、SAStrutsドキュメントにもこのblogでも書いていますが、1つのEntityに閉じたロジックならEntityに記述するということでいいと思います。複数のユースケースで使うからこそ、Entityに書く意味がありますね。個別のユースケースでしか使わないなら、Actionに書いたほうがいいと思います。

LogicにかくのかEntityに書くのかの判断は、1つのEntityに閉じているかで判断するとわかりやすいと思います。複数種類のEntityにまたがっているロジックは、制御ロジックととらえて、Logicに持たせるのが基準が明快なのでわかりやすいのではないでしょうか。

dewadewa 2008/02/07 20:25 Logic以外にUtilやEntityも複数のユースケースで使われると
思いますが、これらの使い分けについてはどうでしょうか?
(感覚的にはできそうだけど、どうも言葉にならない)

aufhebenaufheben 2008/02/08 00:16 僕はService層設ける派なんですが、
例外処理をちゃんと書こうと思うと、トランザクション境界の外側の処理とかあるんじゃないでしょうか?

higayasuohigayasuo 2008/02/08 08:40 例外処理とトランザクション境界は関係ないと思いますよ。
今時なら、例外処理は、AOPで処理するでしょうから、インターセプタの順番をきっちり指定するだけです。
手動で例外処理をするなら、そこだけTransactionAttribute.NONEにして、トランザクショナルなメソッドを呼び出せばいい。

dewadewa 2008/02/08 11:02 > 複数種類のEntityにまたがっているロジックは、制御ロジックととらえて、
> Logicに持たせるのが基準が明快なのでわかりやすいのではないでしょうか。

例えば、「売上金額 = 売上.商品.単価 × 売上.数量」を計算するためには、
売上Entity内の商品Entityをたどって get売上金額() というメソッドを作るのは
複数種類のEntityにまたがるのでNGと考える。
代わりに、売上Logicクラスに
calculate売上金額(売上Entity, 商品Entity)メソッドを作るような感じでしょうか?
また、この場合はjsp内での売上リスト処理はループ内でこのメソッドを呼び出す
ような感じでしょうか?

※ レイジーロードがないことを考慮すると、売上Entity内の商品Entityがnullである
  可能性も考慮しなければならないので、後者のようなステートレスなロジックを
  用意する方が賢明な気がしてきました。
  でも、jsp2.0のことをよく知らないだけかも知れませんが、
  ややViewまわりが煩雑になりそうなのが、気がかりです。

higayasuohigayasuo 2008/02/08 13:07 これぐらいの計算なら、JSPで
${f.h(売上.商品.単価 * 売上.数量)}でいいと思うなぁ。
商品がないときは、0って表示されます。
レイジーロードは無関係でしょう。最初から売り上げ金額を計算するとわかっているなら、明示的に商品をジョインすればいいだけです。

dewadewa 2008/02/08 14:20 > これぐらいの計算なら、JSPで
> ${f.h(売上.商品.単価 * 売上.数量)}でいいと思うなぁ。

なるほど。
最近はHTMLテンプレートばかりを使っていたことと、
viewにはロジックを含めるべきではないという考えで
頭が固くなっていたようです。
jspだし、これくらい柔軟な方がやり易そうですね。

いろいろ、教えて頂き、ありがとうございました。

mattena31mattena31 2008/02/18 01:19 私仙台のシステムエンジニアです。イベント楽しみにしています!!!