複雑な評価処理を行う仕様オブジェクト。

単純な判定処理はメソッドとしてエンティティ・値オブジェクトに持たせてもよいが、複雑な判定処理はドメインオブジェクトの意図が見えづらくなる。

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);