본문 바로가기
반응형
Design Pattern

[디자인 패턴] CQS(Command Query Separation) Pattern

by brightGarden02 2022. 11. 3.

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 패턴 단점

: 간단한 코드일 경우 복잡하게 구현이 될 수도 있다.

 

댓글


반응형
반응형