推奨レイヤアーキテクチャについて

@DBFlute, DBFlute.NET
http://d.hatena.ne.jp/moonlight_nagara/20090825#p1
に対するリスポンスです。

現場ソリューションの資料にもありますが、大きく二つの
やり方があると思っています。
<A>
全てのDBアクセスロジックを画面に依存しない領域に実装

画面に依存しない領域はテーブルと1:1のクラスなどを示します。
例えば、BehaviorのGapクラスにじゃんじゃん独自メソッドを作って、
全てのConditionBeanはここでnewされるようにする。
もしくは、Daoクラスを独自に作って、全てのConditionBeanは
ここでnewされるようにする。(Daoは画面に依存しない)
<B>
画面依存DBアクセスは画面依存領域に実装し、再利用DBアクセスは
画面に依存しない領域に実装

画面依存領域は例えばPageクラスやActionクラスなどを示します。
また、ServiceクラスはFacadeクラスもそれに含まれます。
要はDBアクセスのクライアントクラスたちです。
ここではPageクラスなのかServiceクラスなのかは区別しません。
画面に依存しない領域は「A」で説明した通りです。
別にどちらもでちゃんと実装マネジメントすれば問題なく開発は
できるでしょう。どっちじゃないといけないというレベルの問題
ではないです。それぞれ特徴があって向き不向きがあります。

「A」は「DBアクセスを隠蔽する」という概念的な特徴があります。
DBのことはDBレイヤで全て解決する。DBアクセスの仕方が変わっても
画面クラスには影響がないという特徴があります。
(ただ、O/Rマッパに依存したEntityをそのまま戻すと完全ではない)

「B」はDBアクセスは隠蔽しません。その画面はDBアクセスを
したいのだからDBアクセスをしてる、という感じです。
「画面依存DBアクセス」は画面依存ロジックなんだから
画面依存の領域に実装する、という考え方です。
(他の画面で再利用されては困るDBアクセスは自分のところで実装)

「A」
メリット:
o 画面がDBアクセスの方式(O/Rマッパ)に依存しない
o DBアクセスが1レイヤに集中する

デメリット:
o 画面依存DBアクセスが混在して混乱を招きやすい
 - 画面依存DBアクセスを間違って再利用される可能性がある
 - 「LazyLoad」が無いO/Rマッパだとなおさら混乱しやすい
o 引数をどうする問題がある(DTOを作る?)

「B」
メリット:
o 画面依存DBアクセスと再利用DBアクセスが区別される
 - 画面依存DBアクセスを間違って再利用される可能性はない
 - 「LazyLoad」が無いO/Rマッパに合う

デメリット:
o 画面がDBアクセスの方式(O/Rマッパ)に依存する
o DBアクセスがレイヤにばらける
o 縦割りの開発方式に相性が悪い

どっちもどっちなので、採用した方のデメリットをマネジメントで
ちゃんとカバーすれば良いという感じです。
「A」の方が「再利用の判断がしやすい」と思われる部分もありますが、
あんまりうまくいってるのを見たことは無いです。再利用マネジメントを
しっかりやるのは「A」も「B」も同じかと。

で、DBFluteでどっちが向いているかと言えば、
作者の立場から言えば、「B」が向いていると思います。

なぜなら、DBFluteは「LazyLoad」がないからです。
DBFluteはそれぞれのDBアクセスしたい人(クラス)が
「何を欲しいのか」を明示的に指定します。
そのコンセプトに関してはDBFluteの基本的な特徴なので
ここでは説明は割愛します。
「LazyLoad」がなしで「A」のやり方を採用したときによくあるのが、
Where句が同じで取得したいもの(setupSelect)が違う場合に、
再利用したいがために同じメソッドにして、引数で無理矢理
取得したいものを独自に選択できるようにしたり、最悪なのが
割り切って最小公倍数を取得するようにしちゃったりなど、
今までうんざりするほどこれにハマってる人たちを見て来ました。
この場合「DBFluteをラップして独自のO/Rマッパを作ってる」のと
同じことです。なんのためにConditionBeanという目的指向の
APIを作ったんだか。DBFluteの良さが死んでしまいます。

無論、メソッドを一つにまとめずに画面のDBアクセス毎にメソッドを
作って、内部でいい感じに再利用するようにすればいい感じですが、
他の画面で再利用されては困るDBアクセスが再利用される事件とか、
そのメソッド名でいつも悩む問題を考えると、DBFluteの安全指向の
ポリシーを考えると、やはり相性が良いとは言えません。

但し、外だしSQLに関しては、テーブルと1:1の領域にSQLファイルを
配置する形になっているので、その部分は限定的に「A」です。
これはConditionBeanが存在することが前提で、外だしSQLは
アプリケーションの1割2割と比較的少ない数になるため、「A」の
デメリットがあまり発生しないことによる割り切りです。
逆に外だしSQLは一箇所にまとまるメリットを享受しています。
無論、その外だしSQLの呼び出しに関しては「B」方式で実装。
(BehaviorQueryPathのタイプセーフを割り切れば完全Bも可能です。)
ただ、一つ「B」には決定的なデメリットがあります。
縦割りの開発方式に相性が悪いです。
Pageクラスをひたすら作る人と、Daoクラスをひたすら作る人を
分けて開発を進めていくような場合です。

ここはちょっと個人的な思いになりますが、縦割りで悲惨な開発を
何度も見たことがあり、自分は大規模でもセル方式の方が良いと
思っています。要求されるスキルセットがちょっと高くなりますが、
大規模ならなおさらちゃんと教育期間設けて補うようにするべし。
縦割りのプロジェクトで今まで確実に発生していた現象は、
「その画面に関して皆が無責任になってしまう」ということです。
要は、これは一つの画面で複数の人が実装をしている状態なのです。
場合によっては仕様も複数の人で設計されていたりなど。
当然、連携をするわけですが、その連携はなかなかうまくいかないものです。
結合テストのときも、例外が発生したときに、発生した箇所の実装者に
担当が振られますが、色々なレイヤが絡み合って最終的な例外になっている
だけなので、「俺のところじゃない」、「俺のところじゃない」と担当を
たらい回しにすることがしばしば。
無論、これもマネジメント次第で解消できますが、マネジメント次第なの
であれば、セル方式の方を採用してそっちのデメリットを解消するように
マネジメントした方が建設的なんじゃないかと考えます。
長くなりましたが、とりあえず
「どちらかと言えばDBFluteはBの方が」
という作者の思いがあるということで。
で、データベース操作の共通化に関して

まずは、Where句に関しては既にあるとおり、
ConditionQueryのGapクラスに再利用条件を定義して、
業務的な名前で再利用できるようにするのが良いです。

ごっそりまるごとDBアクセスを再利用したい場合は、
まずは手軽なところでBehaviorのGapクラスがお奨めです。
無論、XxxServiceとかXxxLogicとか別のクラスにしても
構わないと思いますが、その場合はDBアクセスの再利用という
よりかは、ビジネスロジックの再利用という意味合いが強いですね。
実際は自分は
 DBアクセスの再利用 -> BehaviorのGapクラス
 ビジネスロジックの再利用 -> Logicクラス
というようにマネジメントして開発運用したことあります。