CQRS는 명령과 조회의 책임을 분리하는 설계 패턴이다. 전통적인 CRUD 기반의 시스템에서는 같은 데이터 모델과 저장소를 사용하여 읽기와 쓰기를 처리하지만 CQRS는 이를 별도의 모델과 서비스로 분리하여 독립적으로 운영한다.
Command (쓰기 작업)
Query (읽기 작업)
1) 성능 최적화
2) 확장성 증가
3) 유지보수성 향상
1) 복잡성 증가
2) 데이터 일관성 문제 발생 가능
@RestController
@RequestMapping("/orders")
public class OrderCommandController {
private final OrderService orderService;
public OrderCommandController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public ResponseEntity<String> createOrder(@RequestBody OrderRequest request) {
orderService.createOrder(request);
return ResponseEntity.ok("Order Created");
}
}
@Service
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@Transactional
public void createOrder(OrderRequest request) {
Order order = new Order(request.getUserId(), request.getProductId());
orderRepository.save(order);
}
}
@RestController
@RequestMapping("/orders")
public class OrderQueryController {
private final OrderQueryService orderQueryService;
public OrderQueryController(OrderQueryService orderQueryService) {
this.orderQueryService = orderQueryService;
}
@GetMapping("/{orderId}")
public ResponseEntity<OrderResponse> getOrder(@PathVariable Long orderId) {
OrderResponse order = orderQueryService.getOrderById(orderId);
return ResponseEntity.ok(order);
}
}
@Service
public class OrderQueryService {
private final OrderRepository orderRepository;
public OrderQueryService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public OrderResponse getOrderById(Long orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new RuntimeException("Order not found"));
return new OrderResponse(order.getId(), order.getUserId(), order.getProductId());
}
}
CQRS는 종종 이벤트 소싱(Event Sourcing)과 함께 사용됨.
Event Sourcing이란?
CQRS + Event Sourcing을 활용하면?
CQRS는 대규모 시스템에서 성능 최적화와 확장성을 고려할 때 유용한 패턴이다. 하지만 단순한 CRUD 애플리케이션에서는 과도한 설계가 될 수도 있으므로 신중하게 적용해야 한다.
데이터 일관성을 유지하기 위해 Eventual Consistency를 이해하고 적절한 이벤트 처리 전략을 마련하는 것이 중요하다는 점을 다시 한 번 깨달았다.