ひがさんのBlogのまとめサイト

こんなすばらしいまとめサイトがいまだかつてあっただろうか。
という感動の渦の中、一気読み。

wikiroom.com 閉鎖

自分用に、感銘を受けたところ、疑問点などポイントをピックアップしてまとめておこうと。引用先の改行とかスタイルとかは適宜修正してます。

なんか、
すでにお前のいる場所は我々は1年前に通過しているッ
という感じで、ここ最近悩んでることとか、すでに議論されつくしている感が満載で、ワタクシちょっと非常にあせっております。

どの記事も内容が濃い上に記事の数が半端ではないため、まずはくーす時代の記事について追っかけてみます。

例外

catch禁止。間違いない。
2004-06-08 - ひがやすを blog

うおお。強気だ。断言だ。
そうか、やっぱり最近のダイコン時代の流れはそうなっていくんですねぇ。
原則実行時例外ってのも結構衝撃でした。

「例外をキャッチしてすぐロギング」という方針でやっています。
呼び出し元で2重にエラーがロギングされても、さほど問題はないと思います。
2004-06-09 - アガテナ

ふーむ。うちとはだいぶ違うなぁ。
「最後にcatchする箇所で責任もってロギングせよ!」という方針でいっているけど、これは結局メイン実装者が自分自身だからうまくいってるだけかぁ。
多人数でどーんといくとやっぱうまく回らない気がするしなぁ。

『ThrowsLoggingInterceptor の log も 設定ファイルで Injection すると良いのかもしれませんね。』
2004-06-09 - アガテナ(同)

いや、それはいらないでしょう。間違いない。断言だ。(まねっこ)

ただ、いつも迷うことが一つ。例外のログをどの層でとるべきなのか。例えば、DAO層で発生した例外は、サービス層、プレゼンテーション層も通ることになり、基本的にどの層でもcatchすることはできる。
OZACC.blog: ダイコン時代の設計手法 - 例外処理

ああ、わかるわかる。チェック例外でアホみたいに逐次例外翻訳&再throwをやっていた身としては、上位層にそんなことまで伝えてもいいの!?みたいな不安な気持ちになりますね。

と、それに対してひがさん↓。

例外のログを書き出しは、次の場所が考えられると思います。

  1. 例外をthrowするクラス
  2. プレゼンテーション層でサービスを呼び出すクラス(例えばStrutsのAction)
  3. プレゼンテーション層から最初に呼び出されるサービス層のメソッド

1は自分達でthrowしない例外もログをとる必要があるので中途半端。
2と3は呼び出す側か呼び出される側かの違いですが、3の方がお勧めです。
3はプレゼンテーション層に関係無くログをとることができるためです。
2004-06-10 - ひがやすを blog

なるほど。あまり気にしない方向なんですね。
で、ロギング箇所はサービスオブジェクトですか。
そうすると、サービスオブジェクトに対しては、トランザクションと例外処理の2種類のアスペクトを仕掛けるわけですな。

データモデリング

データモデリングは、

の2つのアプローチが考えられます。
どちらを選択するのかは好みの問題なのですが、DOAの方をお勧めします。
DOAはER分析という確立した方法があるのに対し、OOAによるモデリングは、いまだ確立したものがなく、試行錯誤しなければならないためです。
OO原理主義に陥ることなく、常に現実的であれ
2004-06-09 - ひがやすを blog

なるほど、ER分析は覚えておいたほうがよさそうだ。
本を買ったけどきちんと読んでなかったな。

業務ロジック

そうなのかっと感心するとともに、実は当惑…。データとそのデータを扱う処理をひとつのものとして扱うのがオブジェクト指向設計であり、保守を容易にするにはそれを目指すべきだと思い込んでいました。
はてなダイアリー - taediumの日記

関連のあるデータと振る舞いを1つに集めることで、凝集度を上げ結合度を下げることよって保守が容易になるこれはもちろん真実だと思います。しかし、次の重要な点が
これまでは語られていないんじゃないかと思います。
振る舞いは最も結びつきの高いオブジェクトにおくべきだ
2004-06-10 - ひがやすを blog

かなりの衝撃発言。

φ(。_。)メモメモ

永続化のロジックについても考えてみましょう。
例えば、従業員番号で従業員を検索するというロジックを考えてみましょう。
このロジックが従業員エンティティに存在するのは明らかに変です。
このロジックを呼び出すのは、従業員オブジェクトを手に入れる前の話だからです。
エンティティそのものを更新したり削除したりするロジックはどうでしょうか。
これはエンティティにあっても良いのかもしれません。
しかし、同一エンティティに対する検索と更新のロジックが別々の場所に存在すると保守性が悪くなります。
永続化ロジックをエンティティにではなく、永続化層に置くほうが保守性が高くなると思います。
2004-06-10 - ひがやすを blog(同)

こういう風に説明されると説得力があるなぁ。
DAO呼び出しロジックをエンティティに含めるかどうかでよく悩む。
エンティティの利用クラスがエンティティクラスとは別にDAOにも依存していいのか、エンティティクラスだけに依存しておいて、DAOはエンティティクラス内で呼ぶようにしようか、とか。
依存性が増えるほど、保守性が高まるっていうし。
でも、上記の考え方でいえば、普通にDAOの存在をアピールして使ってもらった方が全体的な保守性があがる、ということになりそう。
実際、DAOに新しい検索メソッドが追加されるたびに、エンティティの修正が必要とかいうのはおかしいな。うん。納得。


UI層/プレゼンテーション層

UI層のモデルはエンティティ(ドメインオブジェクト)ではない。
間違いない
〜(省略)〜
UI層のモデルをUIのフレームワークに適したものにすれば、こんな悩みとはおさらばです。あーすっきり。
2004-06-11 - ひがやすを blog

JSFであれば、backing-bean=UI層のモデル、と思えばいいってことですかね?
結構ここのマッピングに悩んだので。

UIに対応するのJavaBeansをどこで作るべきか?これ、悩みます。2つ考えました。じつはどっちもはずれだったりして…。

  1. 画面に対応するJavaBeansはエンティティ(ドメインモデル)と同じように扱う。つまり永続化層(DAO)で生成し、Service層を経てプレゼンテーション層へわたす。
  2. 画面に対応するJavaBeansは通常のエンティティとは区別してプレゼンテーション層で作成する。プレゼンテーション層はいくつかのエンティティをサービス層から受け取ってJavaBeansを組み立てる。

何が悩みどころかといえば、1の場合サービスやDAOが特定の画面に特化してしまうけどいいのか。2の場合だと、プレゼンテーション層でJavaBeans を組み立てるのは煩雑なのでは(DAOの中で必要な情報に全てにアクセスしてJavaBeansにマッピングしたほうが楽なのでは)、ということです。
2004-06-10 - taediumの日記

ふーむ。自分は1派なのかな?
完全に委譲するだけのサービス層オブジェクトとDAO対がそこら中に散乱してたり。
というより、ほとんど振る舞いを持たないJavaBeansをすべての層で持ちまわって使ってたから、ここでの前提から外れるか。

とりあえず、これに対してひがさん↓。

個々の画面に対応するJavaBeansをどこでつくるか?ですが、
UI層のモデル(was JavaBeans)のインスタンスは、
UIフレームワークの方で作成され、StrutsのAction等で、
UI層のモデルとエンティティの変換をおこなうってことで
良いんじゃないかと思ってます。
正解ということではなく、あくまでも私の考えですが。
2004-06-11 - ひがやすを blog

つまり、案2支持ってことでFA?

そういえば関係ないかもしれないが、くーすだったか別の記事で、外部リソースに永続化する必要があるデータがエンティティであり、それ以外のデータは単なるDTOだ、というような話があったような。
後者のような単にDBに保存しておいて参照するだけというようなJavaBeansのことはなんと呼ぶのがいいんだろうか。
更新しないから永続化が必要とはいわんだろうし、エンティティじゃあるまい。
DTOとしても利用されるんだろうけど、なんかそれ自体をあらわすぴったりした言葉がないものか。エンティティ(ドメインモデル)とかプレゼンテーションモデルみたいな。
たいした操作もなくデータを運ぶだけだから、DTOでいいのかなー。
いいのか。
それでいい気がしてきた。
…じゃ、それで。

yuumi3 『「個々の画面に対応するJavaBeansをどこでつくるか?」ですが、私も昨年Strutsで作った2つの小規模なアプリで 1. 2. 両方やってみました。 1.の方はスッキリできますが、つまらないクラスがいっぱい ^^); 2. はコードは少ないですがDAOを継承・委譲した表示用の怪しいクラスが ^^); 悩ましいところです・・・』
2004-06-11 - ひがやすを blog

経験談は参考になるなー。

コントローラの役割は以下のようなものがあると考えています。

  1. UI層のモデルとエンティティの相互変換を行う。
  2. サービス層のロジックを起動する。
  3. UI層固有のロジックを実行する。
    • ファイルのアップロード・ダウンロードなど

2でサービス層のロジックが複数に分割されている場合にプレゼンテーション層でロジックの制御を行ってはいけません。
イベント(Submitやリンクのクリック)が起きたとき、UI層はサービス層の1つのメソッドを呼び出すだけにします。
UI層から呼び出されたサービス層のメソッドがロジックの制御を行います。
ロジックの制御はユースケースに起因するものだからです。
2004-06-18 - ひがやすを blog

だいぶ見えてきたと思ってたけど、この記事でちょっと今までの理解が怪しげに。
具体的に考えるためにJSFを使うとしてみる。
JSFでは、(ユースケース固有の)コントローラ=backing beanだ。

で、

2でサービス層のロジックが複数に分割されている場合にプレゼンテーション層でロジックの制御を行ってはいけません。
イベント(Submitやリンクのクリック)が起きたとき、UI層はサービス層の1つのメソッドを呼び出すだけにします。
UI層から呼び出されたサービス層のメソッドがロジックの制御を行います。

ということは、backing beanのメソッドは単にサービスオブジェクトの1メソッドに委譲するだけになるわけだ。
JSFの場合、successとかfailureとかのnavigation-ruleで定義したview-idを指定するのはbacking beanの責務かと理解してたんだけど、これもサービスオブジェクトの責務になるっていうこと?
そうするとサービスオブジェクトは、UI層(プレゼンテーション層)よりも下位レイヤであるサービス層で、UIの概念であるview-idを意識しないといけなくなるのでは?
これでは層間の相互依存にならないですかね。
まあsuccessとかfailureとかならぱっと見view-idには見えないから大丈夫、なんていい分けもできなくもないけど、要はview-idなわけでしょ。
それとも、サービスオブジェクトは結果をview-idとはまったく異なる何らかの手段で通知するのかな?戻り値のエンティティオブジェクトの状態とか、例外とか。
ここはちょうど今結構悩んでいたところ。
もう少し読み進めると、きれいに整理されているんだろうか。

あと、

ロジックの制御はユースケースに起因するものだからです。

とあるけど、コントローラ自体もユースケースに対応しているのだから、この理由からいくと、コントローラにロジックの制御が入っても問題ない気がします。
違うか。コントローラはバウンダリ(画面)に対応していて、ユースケースに対応するのはサービスなのか。だからコントローラにロジックが含まれるとまずいわけか。
納得。
でも、画面IDの件はまだ非納得。

〜(全部引用するのもなんなので省略)〜
2004-06-19 - ひがやすを blog

これがくーすの設計方針の要ですな。くーすwikiで見たような。
これだけ明確な設計ガイドラインがあるのに、ロバストネス図を捨ててgoyaに進んだというのは何がきっかけなんだろうか。
読み進めればわかるんだろう。

この辺まで読んできて気がついた。
サービス層=ビジネスロジック層(ビジネスドメイン層)か。
遅ッ。

コンポーネント間の関係

1つのインターフェースに1つの実装クラスしかない場合は、依存関係はダイコンによって自動解決されるので、ダイコンファイルに特に記述する必要はありません。
しかし、自動解決されずダイコンファイルに記述しなければならないものは何をもとに記述するのでしょうか。
私は、ダイコンファイルそのものが設計書だと思ってます。
必要ならXSLTでHTMLに変換する手もあります。
2004-06-11 - ひがやすを blog

ダイコンで注入するためのインタフェースという観点から考えると、原則としてインタフェース:実装クラス=1:1方式で設計したほうが良いのか?
大抵はそれでうまくいくけど、ダイコン以前の設計でいうインタフェースの利用方法であれば、インタフェース:実装クラス=1:Nという関係がでてくるなぁ。
こういう場合の実装クラスとの関係はdiconファイルに明記する必要があるわけか。
具体的にいうと、どのDataSourceを使うかとかそういう場合かな。
で、その依存関係は詳細設計情報だと。

バリデーション

Validationはつぎのようなタイプに分けられるのではないかと思います。

  1. 形式的なもの
    • 文字列を本来のオブジェクトの型に変換するのに必要なValidation
    • xx以上xx以下、xxは必須のようなもの
  2. サービスの事前条件をクリアするために必要なValidation
  3. サービス層で行われる業務ロジック的なValidation

〜(省略)〜
上記1,2,3を現実的に書いてみると、

  1. UIのフレームワークで用意されているValidationの仕組みを使って行う
  2. 上記以外のデータベースにアクセスしないValidation
  3. データベースにアクセスするValidation

というふうにわけてもほとんどの場合大丈夫ではないかと思います。
1はUIフレームワークを使って行います。
2はUI層で行います。プレゼンテーション層のモデルで行うことが多いと思います。ただし、フレームワークによります。
3はサービス層で行います。
ユースケースには、すべてのタイプのValidationが、一緒に記述されているので、どのタイプなのかを見極めることが重用です。
最近のWebアプリは永続化にかかわる部分とValidationにかかわる部分でほとんど占められているので、Validationの扱いを間違えなければ、たいていはうまく良くと思います。
2004-06-14 - ひがやすを blog

なるほど。あまり1とか2とか重視してなかったな。
3ですべてをカバーしてたかも。

テスト

UI層は、自動テストは行わない。目視でのテスト。
2004-06-17 - ひがやすを blog

ですよねー。

エンティティ層は、私の想定ではロジックはないので、
2004-06-17 - ひがやすを blog(同)

ああ、やっぱり。Value Objectみたいなイメージか。
ある程度の振る舞いを実装するとしても、エンティティオブジェクトの基本情報操作みたいなメソッドくらい、みたいな。

永続化層も自動テストを行います。永続化層ではモックを使わず、RDBMSに接続した方がいいと思います。実際にSQLを実行してみないと分からないことも多いからです。
このとき、S2Unitのデータベーステストサポート機能が役に立ちます。
テストに必要なデータは、あらかじめテーブルに入れておくのではなく、Excelなどで用意して、テストの直前にテーブルに挿入し、テストが終わったらきれいに削除しましょう。
S2Unitを使えば簡単にできます。
2004-06-17 - ひがやすを blog(同)

MockInterceptorとS2Unitに興味津々。

ユースケース

〜(引用無し)〜
2004-06-20 - ひがやすを blog
2004-06-21 - ひがやすを blog
2004-06-21 - ひがやすを blog

結論として、

という方向で。


参照実装

http://www.javagen.com/petstore/ を例に考えてみます。
2004-06-21 - ひがやすを blog

ということで、やっと実装とのマッピングが実例として見られる!?

外部設計〜内部設計

UI層のコントローラは、バウンダリに含めることにしました。
そのほうが分かりやすそうなので。
2004-06-22 - ひがやすを blog

はい。わかりやすいです。
フレームワークや書籍ではここあたりの解釈というかマッピングがまちまちでよく混乱します。

ロジック層とは、以前サービス層と呼んでいた層です。
SOAの絡みでややこしくなりそうなので、呼び方を変えました。m(_ _)m
2004-06-22 - ひがやすを blog

こっちの方が個人的にはしっくりきますね。

永続化に関する処理は、永続化層のDaoで実装されます。
Daoはエンティティごとに作成します。
動的なSQLの生成が必要な場合、その実装は永続化層で行います。
S2Daoの場合は特に実装の必要はありません。
条件によって見に行くエンティティが変わるようなエンティティをまたがる動的なSQLは、ロジック層でどのエンティティが対象なのかの判定を行い、エンティティが決まった後は、そのエンティティのDaoに処理を任せます。
2004-06-22 - ひがやすを blog(同)

良いヒント。

AOP

これまでバウンダリから呼び出されるコントロール(システムが提供するサービス)をロジックと永続化(Dao)に分けて処理することをずっと提案してきた。
アーキテクチャ的にはきれいなのだけど、心にはくすぶるものがあった。
だって、たいていの画面ってロジックなんかなくて、Daoをたたけば良いのがほとんどだからだ。
単にDaoに処理を委譲しているロジックでも実際の案件では、仕様書もテストもインターフェースも実装クラスも作らなければならない。
無駄なコストは削らなければならない。
バウンダリからDaoを呼ぶようにしても良いけど、バウンダリにコントロールの実装クラスは意識させたくない。
最初は単純にDaoを呼べば良かったケースが後から仕様が変わり、Logicを入れたくなったときに、バウンダリに影響は与えたくない。
Daoにももちろんコントロールを意識させたくない。
それではどうすれば良いのか。orz

考えましたよ、考えた。そこでAOPですよ。
単に委譲するだけの場合は、DelegateInterceptorを使って処理を透過的に委譲するのです。
2004-06-23 - ひがやすを blog

長いけど大変なことが書かれてるのでほぼ全部引用。
なるほど、そこでAOPですか!
僕も悩んでたんですよ。アーキテクチャ的にバウンダリをDAOに依存させるのは気持ちが悪い。でも、委譲するだけのロジッククラスってナンジャソラーと。
DelegateInterceptorを使うと、S2DaoみたくEmployeeLogicはinterfaceだけあればいいってことですね。これで実装とテストが1つ減る!

      • -

以上「ダイコン時代の設計手法 - Logicがない場合」まで。
続きはまた。

#すごいボリュームだ…。