エヴァンスの分類
http://martinfowler.com/bliki/EvansClassification.html
Eric Evansのエクセレントな著書『Domain Driven Design』では、 我々がしばしば目にする様々なドメインオブジェクトが以下のように分類されている。
- エンティティ:アイデンティティを持ち、時間経過によって形を変えるオブジェクト。「リファレンスオブジェクト」とも呼ばれることがある。
- バリューオブジェクト:属性の組み合わせに意味があるオブジェクト。同じ値(バリュー)を持つ2つのバリューオブジェクトは、どちらもすべての属性が等しいと見なされる。バリューオブジェクトについては、私もP of EAAで触れている。
- サービス:ドメインにおける独立したオペレーション。サービスオブジェクトは1つ以上のサービスを持つ。実行文脈におけるサービスオブジェクト型のインスタンスは、通常は1つだけである。
この分類は私の経験から考えられるものと近い。 問題は、これら3つの分類を正確に定義することができないという点だ。 これは「見れば分かる(見るまで分からない)」という分類方法なのである。
次のような例を手がかりとして分類できるかもしれない。 エンティティは「顧客」「出荷」「賃貸契約」など大きなものである。 バリューオブジェクトは「時間」「お金」「データベースクエリー」など小さなものである。 サービスは「データベースコネクション」「メッセージングゲートウェイ」「レポジトリ」「プロダクトファクトリ」といった外部リソースにアクセスするものである。
エンティティとバリューオブジェクトの明確な違いは、バリューオブジェクトは等価メソッドをオーバーライドする(そこでハッシュ値を持つ)が、エンティティはオーバーライドしないという点である。 なぜなら、同一プロセス上に同じ概念を表すエンティティが複数あるのは困るが、 「5.0」オブジェクトならば複数あっても構わないからである。 バリューは、 プリミティブだったり(両者を区別している言語)、 特別な言語サポートを受けていたり(.NETなど)するが、 それらは必ずしも必要なことではない。 従うべきルールは「イミュータブルにすること」ただそれだけである(そうしないと、そのことが原因でバグが発生し、様々なトラブルが起きる)。 イミュータブルにするということは、 値(たとえば私の身長)を変更するときに身長オブジェクトを変更するのではなく、 新しいオブジェクトと入れ替えるということである。
サービスオブジェクトはグローバル変数やクラス変数(Robert Martinの言うmonostates)、あるいはシングルトンで実装されることが多い。 ★たいていは、ただ1つになるが、その実現方法は様々である。★ 「ただ1つ」というのは「ひとつの処理内において」という意味であり、 マルチスレッド環境では「ひとつのスレッド内で」という意味になる。 いずれにせよ、他のドメインオブジェクトから見えないようにして、実装メカニズムを容易に変更できるようにしなければならない。 Ericは著書のなかで「サービスはステートレスであるべきだ」と述べている。 ただし、彼と話をするなかで、今は必ずしもステートレスではなくてもよい、ということになっている。 もちろん、ステートレスにできるなら、したほうがよい。
ここで問題となるのは、その用語である。 分かりやすいことは分かりやすいのだが、困ったことに、他の意味と取り違えてしまうことがある。 「エンティティ」は一般にデータベースのテーブルやそれに対応したオブジェクトのことを指す用語である。 「サービス」はSOAの意味する”サービス”とかぶっているし、 アプリケーションアーキテクチャの”サービスレイヤ”ともかぶっている。 これらの3つの用語を使うときは、 ドメインモデルの文脈で、 Eric本で述べられている意味で、 使っていることを明確にすべきだろう。 これら3つの用語を使っている人を見たら、 それがどんな文脈で使われているかを注意する必要がある。 かなり面倒くさいことではあるが、 残念なことに他にうまいこと言い換える言葉がないのだ。