3つのARがあります。
ビジネスルール:
現在、私はStudent
コンストラクターに不変式を適用しています:
public function __construct(StudentId $id, Name $name, Guardian ...$guardians) {
if ( $this->hasNoAuthorizedGuardian(...$guardians) ) {
throw new NoAuthorizedGuardianException();
}
// initialized fields
}
しかし、生徒用ARのリポジトリパーツを作成しているときに、「必要な」ガーディアンを取得する際に問題が発生しました。
class StudentRepository {
public function getById(StudentId $id): Student {
// other lines omitted for brevity
return new Student($id, $name, ...$guardians);
}
}
私はこれらの質問を念頭に置いて思いつきました:
GuardianRepository
内でStudentRepository
を参照する必要がありますか?答えはノーだと思いますが、DDDは初めてなので有効かもしれません
代わりに、ARにリポジトリへの参照を許可する必要がありますか?これに対する答えもないように感じますが、私がDDDを初めて使用するので、これは有効な場合があります。
不変条件を適切に適用しましたか?または、関連するエンティティを単一のリポジトリでフェッチする必要がないように、ドメインサービスに配置する必要がありますか?
2番目のアプローチを実行すると、これが可能になります。
class StudentService {
public function getById(StudentId $id) {
$student = $this->studentRepo->getById($id);
$guardians = $this->guardianRepo->getByStudentId($id);
$student->guardians(...$guardians);
return $student;
}
}
それを行うことの短所は、不変条件を別のレイヤーに転送したので、私のエンティティは貧血ドメインエンティティになります。
私が思いついた別の可能な解決策は遅延読み込みですが、関連するエンティティをいつ、どのようにフェッチするかなど、その方法がわかりません。
TL; DR:ルートエンティティが別のルートエンティティへの参照を必要とする場合、どのように取得できますか?
[〜#〜]編集[〜#〜]:
明確にするために、Guardianには連絡先や他のいくつかの値オブジェクトなどの独自のエンティティがあることについては触れませんでした。それらを管理するものである必要があるため、ARと見なされるにはそれで十分だと思います。
ここでの生徒と保護者の参照は、今学んだばかりの境界コンテキストです。
「学生は保護者を登録する必要があるか、法的に登録できない」などのビジネスルールに従って、エンティティが「無効」な状態であっても問題ありません。
これらのルールは、変更および例外的なケースの対象となります。プログラムがそのタイプのオブジェクトをインスタンス化できないようにするのではなく、ルール違反などのフラグを立てたい場合。
この場合、アプリケーションは単に両方のARリポジトリを参照できます。ARは、検索可能なsudentId/guardianIdプロパティを持つか、別のサービスで関係を持つことができます。
ここで、「無効」が単なるビジネスルールではなく、エンティティの性質に固有である場合、たとえば、猫には0〜4の脚があり、猫の速度は正確な数に依存します。次に、脚は猫のARの一部である必要があります。
猫に5つのレッグがある場合、Cat.Speedによってプログラムがクラッシュします。猫のレッグの数を変更すると、その速度が自動的に更新されるか、プログラムから誤った出力が得られます。
CatとLegを別々のARに配置することは問題があり、Cat.Speedは結果を返す前にLegRepoを呼び出さなければならず、Catの速度を更新せずにLegを追加できました。
生徒と保護者がどちらのケースに該当するかを決定する必要があります。 ARが1つある場合は、Student.RemoveGuardian(id)
を使用して、最後の保護者の削除を許可しないようにできます。
または、ルールを強制することもできますが、別のアプリケーションがビジネスルールに従わずにGuardianRepoから直接ガーディアンを削除する可能性を許可するGuardianRemovalService.RemoveGuardian(studentId,guardianId)
を持つ方が賢明でしょうか?