본문 바로가기

프로젝트/고민

CQRS패턴 도입 이야기: CQRS 구현 방법

도입부

저번 글에서 왜 CQRS를 이용해야 하는지에 대해 알아봤다. 본 글은 이전 글에 이어서 CQRS를 구현하는 방법에 대해서 알아보고자 한다.


구현 형태

일반적으로 다음 그림과 같은 형태로 구현이 된다.

 

 

 

DB 개념은 조회와 명령의 DB가 같을 경우 다를 경우를 의미하고

 

프로세스 개념은 조회와 명령이 같은 서버에 있는지 다른 서버에 있는지를 의미한다. ㅡ> 마이크로 서비스 아키텍처(MSA) 구조와 연관되어있다.

 

 

1. 같은 프로세스 같은 DB

 

가장 단순한 경우며, 코드적으로만 분리한 경우이다.

 

2. 같은 프로세스, 다른 DB

 

DB도 분리했기 때문에 DB 간에 일관성을 위해 명령 DB 변경시 변경이 전파되어야 한다.

 

3. 같은 프로세스, 같은 DB, 다른 테이블

 

이 경우 조회용 테이블을 따로 두어 성능을 올린 경우이다.

 

4. 다른 프로세스, 다른 DB

 

 

유행하는 아키텍처인 MSA와 유사한 구조를 가지며, DB와 프로세스 모두 분리된 경우다.

 

 

다른 DB로 변경 전파하기

 

 

위의 그림과 같이 크게 3가지로 나눌 수 있다. (여기에 메시지 큐를 응용할 수 있다.)

 

1. 명령이 직접 Query를 수정하는 경우

 

코드를 작성하면 다음과 같다. db2.save() 대신 메시지 큐에 데이터를 전송할수도 있다.

@Transactional
public void save(Entity entity) {
	db.save(entity);
    db2.save(entity.toDb2())
}

 

장점

  • 구현이 비교적 쉽다

단점

  • 쿼리 db나 메시지 큐에 문제가 발생하면 쿼리 db에 반영 되어야 할 데이터가 유실될 수 있다.
  • 또한 쿼리 db나 메시지 큐의 문제 때문에 명령을 수행하는 기능이 에러가 날 수 있다

 

2. 전파기를 통해 변경을 전파하는 경우

 

장점

  • 변경 내용을 기록하고 별도의 전파기를 이용해 변경내역을 전달하기 때문에 변경 내역이 유실되지 않는다

단점

  • 전파기를 따로 구현해야 한다는 단점이 있다.

 

3. db가 제공하는 cdc를 사용하는 경우

 

- db의 바이너리 로그를 읽어서 변경 데이터가 무엇인지 확인하고 변경된 데이터를 쿼리쪽에 전달하는 방식이다.

 

장점

  • 2번 방법과 비슷하지만 명령쪽 코드에서 변경 내역을 따로 저장하지 않아도 돼서 명령 코드가 단순화 된다.

다른 DB 사용시 주의사항

  • 데이터 유실
    실시간 인기 게시글 같은 데이터는 유실되더라도 비교적 치명적이지는 않지만 주문 같은 데이터는 유실되면 큰일 난다.
  • 허용 가능 지연 시간
    주문을 완료했지만 조회 저장소에 전파가 늦어 주문 정보가 조회되지 않는다면 치명적이다.
  • 중복 전달
    쿼리 DB에 데이터가 제대로 전달 안됐을 때 재전송 하는 수단이 제대로 작동하지 않아 제대로 전달이 됐음에도 재전송하는 경우 문제가 발생한다.

 


마치며

CQRS 패턴을 구현하는 방법을 간단하게 살펴봤다. 내 프로젝트에서 적용한 부분은 같은 프로세스에서 다른 DB를 사용한 경우며, 전파기를 통해 명령을 전파했고, Kafka를 이용해 전파와 조회 저장소 사이에 메시지 큐를 뒀다. 다음 글에선 Kafka에 대해 간략하게 살펴보고 적용하는 방법을 작성해볼 생각이다.