CQS(Command Query Separation) Pattern은 소프트웨어 디자인 패턴 중 하나이다.
다음과 같은 이유로 나눈다.
Command: 객체의 내부 상태를 바꾼다.(생성, 수정, 삭제)
Query: 객체의 값만 반환한다.(조회)
예시로
Product 도메인에 대해서
서비스를 ProductCommandService, ProductQueryService로 나눈다.
예시 코드
ProductCommandService
어노테이션 @Transactional
Command: 객체의 내부 상태를 바꾼다.(생성, 수정, 삭제)
@Service
@Transactional
@RequiredArgsConstructor
public class ProductCommandService {
private final ProductRepository productRepository;
private final PostKeywordService postKeywordService;
private final ProductTagService productTagService;
private final PostTagService postTagService;
private final ProductQueryService productQueryService;
public ProductDto create(Member author, String subject, int price, long postKeywordId, String productTagContents) {
PostKeyword postKeyword = postKeywordService.findById(postKeywordId).get();
return create(author, subject, price, postKeyword, productTagContents);
}
public ProductDto create(Member author, String subject, int price, String postKeywordContent, String productTagContents) {
PostKeyword postKeyword = postKeywordService.findByContentOrSave(postKeywordContent);
return create(author, subject, price, postKeyword, productTagContents);
}
public ProductDto create(Member author, String subject, int price, PostKeyword postKeyword, String productTagContents) {
Product product = Product
.builder()
.author(author)
.postKeyword(postKeyword)
.subject(subject)
.price(price)
.build();
productRepository.save(product);
applyProductTags(product, productTagContents);
return new ProductDto(product);
}
public void modify(ProductDto productDto, String subject, int price, String productTagContents) {
Product product = productQueryService.findProductByProductDtoId(productDto.getId());
product.updateSubject(subject);
product.updatePrice(price);
applyProductTags(product, productTagContents);
}
ProductQueryService
어노테이션 @Transactional(readOnly = true)
Query: 객체의 값만 반환한다.(조회)
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ProductQueryService {
private final ProductRepository productRepository;
private final ProductTagService productTagService;
private final MemberRepository memberRepository;
public List<ProductDto> findAllForPrintByOrderByIdDesc(Member actor) {
List<Product> products = findAllByOrderByIdDesc();
loadForPrintData(products, actor);
return products.stream()
.map(o -> new ProductDto(o))
.collect(toList());
}
public List<Product> findAllByOrderByIdDesc() {
return productRepository.findAllByOrderByIdDesc();
}
private void loadForPrintData(List<Product> products, Member actor) {
long[] ids = products
.stream()
.mapToLong(Product::getId)
.toArray();
List<ProductTag> productTagsByProductIds = productTagService.getProductTagsByProductIdIn(ids);
Map<Long, List<ProductTag>> productTagsByProductIdMap = productTagsByProductIds.stream()
.collect(groupingBy(
productTag -> productTag.getProduct().getId(), toList()
));
products.stream().forEach(product -> {
List<ProductTag> productTags = productTagsByProductIdMap.get(product.getId());
if (productTags == null || productTags.size() == 0) return;
product.getExtra().put("productTags", productTags);
});
}
public List<ProductTag> getProductTags(String productTagContent, Member actor) {
List<ProductTag> productTags = productTagService.getProductTags(productTagContent);
loadForPrintDataOnProductTagList(productTags, actor);
return productTags;
}
CQS 패턴 장점
: 데이터 변경 이슈가 있을 때, 변경이 발생하는 Command 관련 클래스를 찾아보면 된다.
유지보수하기 좋은 코드가 된다.
ProductQueryService의 경우 조회만 하기 때문에 단일책임원칙(SRP: Single Responsibility Principle)을 지킬 수 있다.
CQS 패턴 단점
: 간단한 코드일 경우 복잡하게 구현이 될 수도 있다.
'Design Pattern' 카테고리의 다른 글
[디자인 패턴] 전략(Strategy) 패턴 (0) | 2024.02.08 |
---|---|
빌더 패턴(Builder Pattern) 쓰는 이유, 장점, 단점, 사용법 (0) | 2022.11.27 |
댓글