5.1 시작에 앞서

CQRS는 명령 모델과 조회 모델을 분리하는 패턴이다. 명령 모델은 상태를 변경하는 기능을 구현할 때 사용하고 조회 모델은 데이터를 조회하는 기능을 구현할 때 사용한다.

5.2 검색을 위한 스펙

검색 조건을 다양하게 조합해야 할 때 사용할 수 있는 것이 스펙이다. 스펙은 애그리거트가 특정 조건을 충족하는지를 검사할 때 사용하는 인터페이스다.

public interface Speficiation<T> {
	public boolean isSatisfiedBy(T agg);
}

스펙을 리포지터리에 사용하면 agg는 애그리거트 루트가 되고, 스펙을 DAO에 사용하면 agg는 검색 결과로 리턴할 데이터 객체가 된다.

public class OrdererSpec implements Specification<Order> {
	private String ordererId;

	public OrdererSpec(String ordererId) {
		this.ordererId = ordererId;
	}

	public boolean isSatisfiedBy(Order agg) {
		return agg.getOrdererId().getMemberId().getId().equals(ordererId);
	}
}

리포지터리나 DAO는 검색 대상을 걸러내는 용도로 스펙을 사용한다. 만약 리포지터리가 메모리에 모든 애그리거트를 보관하고 있다면 다음과 같이 스펙을 사용할 수 있다.

public class MemoryOrderRepository implements OrderRepository {
	
	public List<Order> findAll(Specifincation<Order> spec) {
		List<Order> allOrders = findAll();
		return allOrders.stream()
										.filter(order -> spec.isSatisfiedBy(order))
										.toList();
	}
}

리포지터리가 스펙을 이용해서 검색 대상을 걸러주므로 특정 조건을 충족하는 애그리거트를 찾고 싶으면 원하는 스펙을 생성해서 리포지터리에 전달해 주기만 하면 된다.

// 검색 조건을 표현하는 스펙을 생성해서
Specification<Order> ordererSpec = new OrdererSpec("madvirus");
// 리포지터리에 전달
List<Order> orders = orderRepository.findAll(ordererSpec);

모든 애그리거트 객체를 메모리에 보관하기도 어렵고 설사 메모리에 다 보관할 수 있다 하더라도 조회 성능에 심각한 문제가 발생하기 때문에 실제 스펙은 이렇게 구현하지 않는다.

5.3 스프링 데이터 JPA를 이용한 스펙 구현

5.4 리포지터리/DAO에서 스펙 사용하기

5.5 스펙 조합

5.6 정렬 지정하기

5.7 페이징 처리하기

5.8 스펙 조합을 위한 스펙 빌더 클래스

5.9 동적 인스턴스 생성

5.10 하이버네이트 @Subselect 사용