[7]エンティティのバリデーションを検討
次に、エンティティのバリデーションについて詳しく見ていきましょう。エンティティでは「属性単位」「オブジェクト全体」「複数オブジェクト時」という3つの粒度に分けてバリデーションを管理します。ここでは、わかりやすさのため従業員(Employee)というクラスを例に紹介します。
属性のバリデーション
エンティティの属性に値をセットする場合、「空ではないか」「5文字以上か」「30文字以下か」といった妥当性のチェックを行います。不正な値が登録されないために「自己カプセル化」の手法を使い、メソッドやコンストラクタからセッターを呼び出します。これにより、不正な値であればアサーションから違反を示すエラーを発生させることができます。
アサーション(assertion:表明)とは、プログラムの状態に関する前提を検証する仕組みの1つです。アサーションは「assert 年齢 >= 0」のような真偽値を判定する式を含みます。その条件が真とならない場合、想定外である旨を通知します。プログラム中にアサーションを記述することで、効果的/積極的にバグを見つけることができ、プログラムの仕様を明確にして保守を容易にするメリットがあります。
下図は属性単位のバリデーションのイメージとなります。属性のチェックにおいては、バリデーション専任のクラスを作る場合もありますが、ここではドメインオブジェクトにバリデーションを記述しています。
ここでは契約による設計を意識して、事前条件としてアサーションを記述することで、オブジェクトの状態を適切に維持しています。「契約による設計(Design By Contract:DbC)」とは、バートランド・メイヤー氏によるプログラムの安全性を高める手法で、事前条件/事後条件/不変条件という3つの契約種類から構成されます。
- 事前条件(precondition):呼び出し側がメソッド開始時に保証すべき条件
- 事後条件(postcondition):呼び出された側がメソッド終了時に保証すべき条件
- 不変条件(invariant):データが常に(開始時も終了時も)満たすべき条件
これらの満たすべき条件を仕様としてコード中に埋め込むことにより、安全な設計を行うことができます。
オブジェクト全体のバリデーション
オブジェクト全体のチェックでは、実装クラスに対応した「バリデータ」クラスを作成し、適切なタイミングで呼び出すようにします。
IDDDでは、ウォード・カニンガム氏が提唱したCHECKSパターンランゲージの「遅延バリデーション(可能な限り先送り)」が、オブジェクト全体のバリデーションと相性が良いとしています。複雑なオブジェクト全体のチェックを都度行うのではなく、先送りすることでチェックをシンプルにできるためです。
複数オブジェクトの組み合わせ時のバリデーション
複数オブジェクトの状態が妥当かチェックするケースでは、必要な数だけバリデータを用意します。
チェックタイミングを管理したい場合は、ドメインサービス(7章)で制御する方法が紹介されています。チェック可能なタイミングになったら、オブジェクト側からバリデーション可能な旨のイベントをサービス(クライアント)側へ通知して、チェックを実行します。
エンティティの変更管理
なお、エンティティの変更を追跡することは必須ではありません。もしビジネス要件的に変更の追跡が必要であれば、ドメインイベント(8章)やイベントソーシング(4章)を使えばいいでしょう。
最後に
以上、本稿ではDDDのエンティティについて紹介しました。今回はドメインの要件を理解してエンティティを発見し、成長させていく方法を学びました。また、それらをコーディングする方法について紹介しました。次回はDDDの「値オブジェクト」を紹介します。
参考資料
- ドメイン駆動設計を実践するために(Digital Romanticism)
- ドラゴンボールで学ぶオブジェクト指向 改(達人プログラマーを目指して)
- システム設計日記 ICONIXプロセス(システム設計日記)
- 【ICONIX】 まずは、ドメインモデリングから始めよう(T2-Wonderland)
- ドメインモデル貧血症の処方箋(まめログ)
- [リファクタリング]自己カプセル化(ゲームが作れるようになるまでがんばる日記)
- [リファクタリング]Self Encapsulate Field(ストラテジック チョイス)
- DDDの仕様パターン(pospomeのプログラミング日記)
- equals をオーバーライドする時は、常に hashCode をオーバーライドする(The King’s Museum)
- [PofEAA]レイヤースーパータイプ(ストラテジック チョイス)
- Intention-Revealing Interface パターン : わかりやすい名前をつける(システム設計日記)
- 意図の明白なインタフェース-INTENTION-REVEALING INTERFACES(JavaEE勉強会)
- アサーションを使用したプログラミング(Java2 SDKドキュメント)
- 基礎からのCode Contracts(SlideShare)
- ガード句(R.Tanaka.Ichiro’s Blog)
- Specification パターン :複雑な ビジネスルールの表現手段(システム設計日記)
- Practical DDD Specificationパターンの例(PHP Mentors)
- 分類と分解をUMLで表現する(ITmedia エンタープライズ)
- エリック・エヴァンス氏: ドメイン駆動設計はソフトウェア開発に有益か?(InfoQ)
