# Command Bus는 반드시 비동기를 지원해야할까?

## 배경

***CQRS(Command Query Responsibility Segregation) Pattern***은 명령(Command)과 조회(Query)를 분리하여 각각 독립적으로 최적화할 수 있는 강력한 아키텍처 패턴이다. 이를 통해 시스템의 확장성과 유지보수성을 높일 수 있다.

CQRS에서 명령을 처리하는 과정에서 `Command Bus`라는 개념이 등장하는데, 많은 자료에서 이를 비동기 방식으로 처리하는 예시를 주로 다루고 있다. 그러나 반드시 비동기 처리가 요구되는 것은 아니며, 오히려 동기 방식을 기본으로 시작하는 것이 더 적절할 수 있다.

이번 글에서는 Command Bus의 동기 및 비동기 방식에 대한 선택 기준을 알아보고, 어떤 경우에 비동기 방식을 도입하는 것이 적절한지에 대해 다뤄보려한다.

## 반드시 비동기를 지원해야하는가?

**CQRS 패턴에서 Command Bus가 반드시 비동기로 동작해야 하는 것은 아니다.**

실제로 많은 시스템에서 Command Bus는 기본적으로 동기적으로 동작하며, 필요에 따라 비동기 방식으로 전환하는 것이 더 바람직하다.

`반 버논(Vaughn Vernon)의 Implementing Domain-Driven Design` 책에서는 다음과 같이 언급된다.

> "커맨드를 비동기식 메시지로 보내며, 이는 전용 스타일로 설계된 핸들러로 전달된다. 이는 각 커맨드 처리기 컴포넌트가 특정 타입의 메시지를 받도록 할 뿐만 아니라, 커맨드 프로세싱 부하를 처리할 수 있도록 주어진 타입의 처리기를 추가할 수 있다. 그러나 이 접근법에는 좀 더 복잡한 설계가 필요하기 때문에 이를 기본으로 사용하면 안 된다. 일단은 동기식 커맨드 처리기로 선택해 시작하자. 확장성(Scalability)의 요구가 있을 때에만 비동기식으로 변경하자."

즉, **비동기 처리는 기본값이 아니라 필요할 때 적용하는 것이 적절하다.**

## 그렇다면 기본적으로 동기 방식이 적절한 이유는 무엇일까?

### ***일관성 유지가 용이하다***

* 동기적으로 커맨드를 처리하면, 명령 실행 후 즉시 조회 모델이 최신 상태로 유지될 가능성이 높아진다.
    
* 이는 사용자가 커맨드 실행 후 변경 사항을 즉시 확인할 수 있음을 의미한다.
    
    * 예를들어, 고객이 쇼핑몰에서 주문하면 주문 정보가 즉시 반영되어 보여주기를 기대한다. 만약 비동기 방식이라면, 메세지 큐에 의해 주문이 나중에 처리될 수 있으므로 즉시 조회했을 때 아직 데이터가 반영되지 않았을 가능성이 존재한다.
        

### ***트랜잭션 관리가 단순하다***

* 동기 방식에서는 하나의 트랜잭션 내에서 명령을 실행하고 필요 시 쉽게 롤백할 수 있다.
    
    * 예를들어, 은행 계좌 이체를 수행할 때 하나의 트랜잭션으로 송금과 입금을 처리하면, 실패 시 쉽게 복구할 수 있다.
        
* 반면 비동기 시스템에서는 비동기 방식에서는 분산 트랜잭션(Distributed Transaction) 처리가 필요해지는 복잡성이 초래된다.
    

### ***디버깅이 용이하다***

* 동기 방식에서는 문제가 발생한 시점과 원인을 즉시 파악할 수 있다. 요청을 보낸 후 응답이 즉시 오기 때문에, 에러 발생 시 트레이스를 쉽게 따라갈 수 있다.
    
* 반면 비동기 시스템에서는 메시지 지연이나 실패 시 추적이 어려울 수 있다.
    

## 그럼 언제 비동기 방식을 고려해야할까?

### ***확장성이 요구될 때***

* 사용자가 많아지면 동기 방식의 커맨드 처리는 서버 부하를 증가시키고, 응답 속도가 느려질 수 있다.
    
* 커맨드 처리를 메세징 서비스(e.g. Kafka)를 통해 비동기로 분산하면 수평 확장이 쉬워지고, 부하를 분산할 수 있다.
    

### ***고성능이 요구되는 시스템일 때***

* 대량의 트래픽을 처리하는 시스템에서는 동기 방식으로 인해 성능 병목(Bottleneck)이 발생할 수 있다.
    
    * 예를들어, 실시간 데이터 스트리밍이 필요한 시스템에서는 빠른 응답을 위해 비동기 방식이 적절할 수 있다. 대규모 로그 처리 시스테에서 Kafka와 같은 서비스를 이용하여 이벤트를 비동기로 수집하는 사례까 대표적이다.
        

### **즉각적인 응답이 필요하지 않은 경우**

* 일부 도메인에서는 명령 실행 후 즉시 결과를 제공할 필요없는 경우가 있다.
    

위 케이스 외에도 더 다양한 트레이드 오프가 존재할것이라 생각한다. 만약 비동기로 명령을 처리하고 프론트쪽에서는 폴링 기법을 활용하여 처리가 완료될 때 까지 조회를 진행하는 방식을 채택할 수도 있다.

CQRS Pattern는 다양한 비지니스 요구사항을 만족할 수 있는 좋은 기법이라 생각하지만, 이번글에서 다룬 명령 처리 부분 외에도 읽기 모델(Read Model)을 가공하는 부분에서 대게 결과적 일관성 방식으로 구현되어 지연(Lag)과 실패에 대한 핸들링이 필요해진다.
