複雑な評価処理を行う仕様オブジェクト。
単純な判定処理はメソッドとしてエンティティ・値オブジェクトに持たせてもよいが、複雑な判定処理はドメインオブジェクトの意図が見えづらくなる。
public class CircleMembersFullSpecification
{
public bool IsSatisfiedBy(CircleMembers members)
{
var premiumUserNumber = members.CountPremiumMembers(false);
var circleUpperLimit = premiumUserNumber < 10 ? 30 : 50;
return membersCountMembers() >= circleUpperLimit;
}
}リポジトリと組み合わせる
ドメイン知識がリポジトリ に依存することを防ぐ
例)おすすめサークルを探し出すメソッド
以下の例は実装すれば動作するが、リポジトリにドメインルールが流出している。
public interface ICircleRepository
{
public List<Circle> FindRecommended(DateTime now);
}仕様オブジェクトにおすすめサークルの条件を表現すると、リポジトリへのドメインルール流出を防ぐことができる。
仕様オブジェクト
public class CircleRecommendSpecification
{
private readonly DateTime executeDateTime;
public CircleRecommendSpecification(DateTime executeDateTime) {
this.executeDateTime = executeDateTime;
}
public bool IsSatisfiedBy(Circle circle) {
if (circle.CountMembers() < 10) {
return false;
}
return circle.Created > executeDateTime.AddMonths(-1);
}
}︙
var recommendCircleSpec = new CircleRecommendSpecification(now);
var circles = circleRepository.FindAll();
var recommendCircles = circles
.Where(recommendCircleSpec.IsSatisfiedBy)
.Take(10)
.ToList();
return new CircleGetRecommendResult(recommendCircles);