처음에는 특정 클래스에서 boolean
타입의 메소드를 추가하다가, 여러 규칙이 추가됨에 따라서 클래스의 명료함이 규칙을 평가하는 코드 덩어리에 묻혀 이내 사라져 버릴 수 있음
또한 근본적인 의미를 지원하지 않는 도메인 클래스와 하위시스템에 대해 의존성을 가질 수 있음
리팩토링을 목적으로 규칙을 평가하는 코드들은 응용 계층으로 옮겨질 것이며, 단순한 조건 코드로 팽창될 것이고 결국 규칙에 대한 가독성은 떨어지고 만다.
종종 업무 규칙이
ENTITY
나VALUE OBJECT
가 맡고 있는 책임에 맞지 않고 규칙의 다양성과 조합이 도메인 객체의 기본 의미를 압도할 때가 있다.
그렇다고 규칙을 도메인 계층으로부터 분리한다면 도메인 코드가 더는 모델을 표현할 수 없어서 상황이 더 악화된다.
SPECIFICATION
은 다른 객체에 대한 제약조건을 기술하며, 제약조건은 존재할 수도 존재하지 않을 수도 있다.
SPECIFICATION
을 다양한 용도로 사용할 수 있지만 가장 기본적인 개념은 다른 객체가SPECIFICATION
에 명시된 기준을 만족하는지 검사할 수 있다는 것이다.
특별한 목적을 위해 술어와 유사한 명시적인 VALUE OBJECT를 만들어라. SPECIFICATION은 어떤 객체가 특정 기준을 만족하는지 판단하는 술어다.
단순한 명세들을 결합하도록 개념을 확정해서 사용할 수 있다.
@RequiredArgsConstructor
class Specifiaction {
private final LoalDate dt;
boolean isSatisFiedBy() {
//
}
}
SPECIFICATION
을 통해서 규칙을 도메인 계층에 유지할 수 있음Set set = repository.selectSatisFying(new ConcreteSpecifiaction(date));
public class Speicifacation {
private final LocalDate date;
// 기본적인 코드들
public Set satisfyingElementsFrom(Repository repository) {
return repository.selectWhereDueDateIsBefore(date);
}
}
public class Repository {
public Set selectSatisfying(Specification spec) {
return spec.satisfyingElementsFrom(this);
}
}
생성기의 구현을 인터페이스로부터 분리할 수 있다. SPECIFICATION
은 생성할 결과물에 대한 요구사항은 선언하지만 결과물을 생성하는 방법은 정의하지 않는다.
SPECIFICATION
을 통해서 생성기의 결과물을 예상할 수 있음
인터페이스는 생성기에 대한 입력을 정의하는 명시적인 방법이 모델에 포함돼 있어서 테스트하기가 수월함
public interface WareHousePacker {
public void pack(Collection containersToFill, Collection drumsToPack)
}