- CQS는 Bertrand Meyer에 의해 제안되었다
- CQS의 목적은
커맨드와 쿼리를 분리하는 것
이다- command(명령)은 결과를 반환하지 않는다. 시스템의 상태를 변경한다
command
라는 용어대신 컨텍스트에 따라modifier
,mutator
라고 부를 수 있다
- query(질의)는 결과를 반환한다. 시스템의 관찰 가능한 상태를 변경하지 않는다(사이드 이펙트가 없다)
- 사이드 이펙트의 정의는 맥락마다 다르지만 여기서 쓰인 의미는 내부에 변경이 일어나는지 아닌지를 말하는 것이다. 즉, 내부에 변경이 발생하지 않으면
사이드 이펙트가 없는 것
이다. - 예외사항
- CQS의 예외사항으로 좋은 사례는
stack
의pop
이다 - stack의 pop은 가장 최근에 push된 값을 반환하고 stack에서는 제거하는 명령이다
- pop은 command(stack에서 값이 제거되므로 stack의 상태 변경)와 query(stack에 가장 최근에 추가된 값이 반환)가 합쳐진 것이다
- 마틴 파울러는 할 수 있는 한 CQS 원칙을 지키려 하겠지만 stack의 pop과 같이 매우 유용한 상황에서는 얼마든지 CQS 원칙을 깨뜨릴 준비가 되어있다고 말했다
- trade off를 생각하고 유연한 사고로 설계하며 구현하자
- CQS의 예외사항으로 좋은 사례는
- JPA에서 활용하는 방법
- insert는 id만 반환
- insert는 DB의 상태에 변경을 일으키지만 id를 반환한다. 이는 필요에 의해 CQS 원칙을 깬거라고 볼 수 있다.
private EntityManager em; @Transactional public Long save(Item item) { em.persist(item); Item item = em.find(Item.class, id); return item.getId(); }
- update는 아무것도 반환하지 않음
- DB의 데이터에 변경이 발생하지만 아무것도 반환하지 않으므로
command
다
private EntityManager em; @Transactional public void update(Item item) { em.persist(item); }
- DB의 데이터에 변경이 발생하지만 아무것도 반환하지 않으므로
- select는 내부 변경없이 결과값만 반환
- DB의 데이터에 변경이 발생하지 않고 sql query에 대한 결과값만 반환하므로
query
다
@Transactional public Item findOne(Long id) { Item foundItem = em.find(Item.class, id); return foundItem; }
- DB의 데이터에 변경이 발생하지 않고 sql query에 대한 결과값만 반환하므로
- insert는 id만 반환
- command(명령)은 결과를 반환하지 않는다. 시스템의 상태를 변경한다
참고자료
- https://martinfowler.com/bliki/CommandQuerySeparation.html
- Meyer likes to use command-query separation absolutely, but there are exceptions. Popping a stack is a good example of a query that modifies state. Meyer correctly says that you can avoid having this method, but it is a useful idiom. So I prefer to follow this principle when I can, but I'm prepared to break it to get my pop.
- https://hyeonk-lab.tistory.com/43
- 변경(사이드 이펙트): 사이드 이펙트라는 단어가 비슷하면서도 서로 다르게 정의 내려지지만 핵심은 '상태 변경'이다.
- https://www.inflearn.com/questions/27795
- 이게 웹 애플리케이션에서 얻는 이점이 있다기 보다는, 개발 전반에 기본개념으로 깔고 가시는 것이 좋습니다.그렇게 되면 데이터 변경 관련 이슈가 발생했을 때, 변경이 일어나는 메서드만 찾아보면 됩니다.변경 메서드도 변경에만 집중하면 되기 때문에 유지보수가 더 좋아집니다.
- 제가 권장하는 방법은 insert는 id만 반환하고(아무것도 없으면 조회가 안되니), update는 아무것도 반환하지 않고, 조회는 내부의 변경이 없는 메서드로 설계하면 좋습니다.
- 정말 크리티컬한 이슈들은 대부분 데이터를 변경하는 곳에서 발생하지요.
- 이 메서드를 호출 했을 때, 내부에서 변경(사이드 이펙트)가 일어나는 메서드인지, 아니면 내부에서 변경이 전혀 일어나지 않는 메서드인지 명확히 분리하는 것이지요.
'프로그래밍' 카테고리의 다른 글
[DB] Character Set, Collate이란 무엇인가? (2) | 2020.08.07 |
---|---|
세션(session)이란 무엇인가? (0) | 2020.05.07 |
AWS EC2 INSTANCE SCHEDULER로 INSTANCE 자동으로 켜고 끄기 (4) | 2020.03.03 |
스프링부트 - Controller란 무엇인가 (0) | 2018.06.16 |
자바 StringBuilder 사용법 및 사용하는 이유 (5) | 2018.04.10 |