集約をモデリングして、その集約の中のエンティティから可能な限り多くの振る舞いをバリューオブジェクトに移行しようとするとき、より多くの振る舞いが必要になるにつれ、新しいバリューオブジェクトが必要になる。これは、Paul Rayner氏が集約やエンティティ、バリューオブジェクトなどドメイン駆動設計(DDD)の世界の概念を取り上げた一連のブログ記事の中で推奨していることだ。
集約とは、例えば、明細が紐づいた購入注文をモデリングする場合に使う。このようなふたつのオブジェクトが凝集しているのは集約のよい例だ。実際のビジネスは注文の文脈で動き、個別の明細は関係ない。購入注文が集約の元になる。集約はビジネスレベルの概念をサポートし、集約の境界は最大の注文量のようなビジネスルールを強制する。永続化や検索は集約の元に対して行われ、対象は集約全体になる。
Rayner氏によれば、バリューオブジェクトは重要な構成要素だ。理由は、
- モデル内の重要な概念に表現豊な名前を付ける。
- オブジェクトの凝集度を高める。
- 顧客の名前のようなドメインのシンプルな概念にプリミティブを使うことから脱出できる。
- エンティティは識別性を担保することにのみ注力し、振る舞いをバリューオブジェクトに委譲できる。
Rayner氏はData Transfer Objects(DTO)はバリューオブジェクトに似ているが、DTOはあくまで、レイヤ間でデータを転送するのに使われる、と強調する。バリューオブジェクトはビジネスドメインの概念を表現するためのものだ。
Rayner氏はバリューオブジェクトは不変であるべきだとと言う。そして、Joshua Bloch氏を引用している。
クラスは可変にするための十分な理由がない限り、不変にするべきだ。クラスを可変にできないのであれば、可能な限り変更可能性を制限するべきだ。
また、Rayner氏がバリューオブジェクトを不変にするべきだという理由は、次のものがある。
- キーや識別子として使う場合、変更してはならないから。
- スレッドセーフであり、並列処理をシンプルにするから。
オブジェクトを不変にするための技術はプログラム言語によってさまざまだ。フリーランスの開発者であるChris Oldwood氏は、この点について説明をしている。
Rayner氏によれば、バリューオブジェクトをリレーショナルデータベースに永続化するとき、いくつかの選択肢がある。
- バリューオブジェクトのフィールドをエンティティのテーブルにおさめる。この設計はシンプルでバリューオブジェクトのリファクタリングもサポートする。
- 各バリューオブジェクト毎にテーブルを容易する。この設計の場合、バリューオブジェクトのリストにも対応する。
- バリューオブジェクトをシリアライズして、エンティティテーブルのひとつのオブジェクトに保存する。リストを保存するのにRayner氏が推奨する方法だ。JSONのようなテキストフォーマットをサポートするデータベースもあるが、問い合わせやインデックスのサポートに制限がある。シリアライゼーションの結果が大きくなる場合にも問題がある。
ドキュメントデータベースや一部のオブジェクトはRDBに保持し、残りをNoSQLに保持するような組み合わせも選択肢になり得る。現在の多くのドキュメントデータベースは検索列を別の設けなくてもドキュメントに対して問い合わせができる。