📌 사용 계기

우리 프로젝트에서 DB에 접근하여 조회 및 명령(수정,삭제) 를 하는 부분들이 서비스 로직에 많이 섞여 의존하는 관계를 형성하고 있었다. 이를 정리하고 싶어 CQRS 패턴을 공부하고 적용해보고자 정리하게 되었다.

 

 

📌CQRS 패턴이란?  

Command and Query Responsibility Segregation 의 약자로 명령 영역과 조회 영역으로 나누는 아키텍처 패턴을 말한다.  

서로의 책임을 나누어 분리하는 것이 CQRS인데 그럼 여기서 말하는 명령과 조회는 무엇일까?

 

📍명령과 조회

우리는 DB를 다룰때 CRUD ( Create / Read / Update / Delete ) 를 기본으로 사용하게 된다. 근데 CQRS는 여기서 굳이 Read만 빼서 조회라고 하고 나머지를 명령으로 나누어 관리하게 된다. 왜?

 

애플리케이션을 만들고나서 시간이 지나게 되면 다양한 요구 등으로 처음 모습과 달라지게 된다. 이것은 물론 DB도 마찬가지이다. 내가 진행한 프로젝트에서 처음 회원 테이블은 정말 단순하게 이메일, 패스워드, 닉네임 같은 기본 정보만을 가지고 있었는데 프로젝트가 점점 기능이 붙고 커지게 됨으로써 게임에서 이긴 횟수, 플레이 타임 등의 속성이 추가되어 DB도 기본 모델과는 모습이 많이 달라지게 되었다. 

 

이 달라진 모델에서 기존 표현되던 데이터를 내보내려면 재가공이 필요해지게 되었다. 초기에는 별도 가공 없이 모두 조회해 내보낼 수 있는 데이터였다면, 이제는 가릴건 가리고 보일건 보여야 하는 것이다. 회원의 인적사항에 대해서만 보여야 할 페이지에 게임 플레이 관련 데이터를 내보내지 않게 조정하고, 반대로 게임 플레이 관련 페이지에서 갑자기 패스워드 정보가 나오면 안되는 것처럼 말이다.  

따라서 모델이 변함에 따라 저장, 수정, 삭제에 사용하는 모델과 조회할 때 사용하는 모델 간의 차가 생기게 되었고 그래서 이를 나누어 관리하게 되면서 등장한게 CQRS다.

 

 

📍하면 뭐가 좋은데 ?

이렇게 나누면 뭐가 좋을까? 

  • 첫째, 도메인 로직에만 집중할 수 있게 된다

도메인 로직에서 데이터를 변경시키고 조회하는 로직을 분리해내 결국 서비스에 관련한 핵심 로직들만 깔끔하게 남겨 로직을 집중시킬 수 있다. 

  • 둘째, Command 쪽과 Query 쪽의 각자에 맞는 다른 인프라를 구성할 수 있다. 

만약 Command DB와 Query DB를 나누는 방식을 채택하여 사용하면 각각에 맞는 인프라를 선택하여 사용할 수 있어, 성능적으로 향상을 기대해 볼 수 있다. 그리고 Read와 Write의 비율이 1000 : 1 이라고 하니 많은 요청 정도에 따라서도 환경에 대해 생각해볼 수 있게 된다.

 

 

📍그래서 적용은?

CQRS를 적용하는 방법은 크게 3가지가 존재한다.

 

1) Simple CQRS Architecture

데이터 베이스는 분리하지 않고 모델 계층만 분리한다. 데이터 베이스는 분리하지 않기 때문에 성능상 변화는 없다.

 

2) CQRS with separated persistence mechanisms

데이터 베이스를 Command(명령)용, Query(조회)용 DB로 분리하여 그 사이에 별도 브로커를 두어 두 DB간에 동기화시키는 방식. 심지어 조회용 DB를 별도의 다른 저장소를 사용하여 사용하고자 하는 모델에 맞게 조정하여 사용할 수 있어 성능이 향상될 수 있다. 하지만 중간에서 동기화에 관여하는 브로커의 성능과 신뢰성이 매우 높아야 한다는 조건이 붙게된다. 

 

3) CQRS 와 Event Sourcing

이벤트 소싱도 CQRS처럼 패턴의 종류인데 어떠한 이벤트가 발생하면 이 이벤트를 통해 데이터를 조작하는 방식을 말한다. 데이터의 현재 상태만 기록하는 방식과는 달리 데이터에 관련하여 이루어진 이벤트에 대한 히스토리도 별도의 데이터베이스에 저장하는 방식인데 이를 CQRS와 합쳐 사용하는 방식이다. 

 

 

📍해볼 방법은?

아직은 CQRS에 대한 이해가 적고, 데이터 베이스를 하나만 두고 로직을 분리하는 작업만 해볼 거기 때문에 1번 Simple CQRS Architecture를 진행해보려고 한다. 

 


➕ 공부자료

 

CQRS 패턴 알아보기

들어가며 Event Driven Architecture에 대한 경험이 있으시거나, 혹은 그것에 관심이 있으신 분이라면 종종 CQRS라는 단어를 듣게 됩니다. 저 또한 종종 들었습니다. 하지만 정확하게 CQRS가 어떤 것인지

always-kimkim.tistory.com

 

[CQRS] CQRS는 어떻게 적용할까?

어떻게 적용할까? 전통적인 CRUD 시스템은 아래와 같이 계층 구조로 구성되어 있습니다. 이러한 시스템 구조에 CQRS를 적용하기 위한 방법은 크게 3가지가 있습니다. (1) Simple CQRS Architecture 데이터

kellis.tistory.com

 

 

팩토리 패턴

- 객체의 생성을 캡슐화 하는 패턴

- 구체적인 객체의 생성과정을 '팩토리'로 모듈화해 구체적인 부분이 아닌 추상적인 부분에 의존하게 하여 의존 역전의 원칙(DIP)를 활용한다.

- 대표적으로 팩토리 메소드 패턴과 추상 팩토리 패턴이 존재한다.

 

종류 공통점 차이점
팩토리 메소드 패턴 객체의 생성부를 캡슐화해 느슨한 결합
구체가 아닌 추상적인 타입에 의존하게 함(DIP)
상속 통해 서브 클래스에서 팩토리 메소드를 오버라이딩해 객체 생성부 구현
추상 팩토리 패턴 객체의 집합을 생성을 위하 정의를 추상체에 선언하고 하위 구현체에서 세부적인 집합 생성 과정을 구현 (factory method 이용해 구현)

 

 

팩토리 메소드 패턴이란?

상위 클래스가 구체 클래스에 대한 정보 없이 구체 클래스를 생성하게 하는 패턴으로, 하위 클래스가 어떤 객체를 생성할 지 결정하게 한다. 하위 클래스에서 팩토리 메서드를 오버라이딩해 객체를 반환하게 하는 패턴

 

  • 팩토리 메소드란?

객체 생성을 반환하는 메서드

 

  • 사용이유 

- 생성할 객체 타입을 예측할 수 없을때

- 생성 객체 기술 책임을 서브클래스에게 정의하고자 할대 (의존성 제거)

- 객체 생성 책임을 서브클래스에 위임하고 서브클래스에 대한 정보를 은닉할 때

 

  • 장점

- 기존 코드(인스턴스 만드는 과정)를 수정하지 않고 새로운 인스턴스를 다른방법으로 생성하도록 확장

- 병렬적 클래스 계층도를 연결하는 역할을 담당할 수 있음

 

  • 단점

- 클래스가 많아져 클래스 계층도 커진다.

 

 

 

 

 

예시 - 항해를 누가 수료할 지 모를때

  • 수료자 관한 클래스들

수료자 클래스

public abstract class HangHaeGraduate {	// 항해 수료자 클래스
	public abstrack String getPostScrpit();	// 항해 후기 
}

 

수료자 A

public class ES extends HangHaeGraduate {
    @Override
    public String getPostScript() {
    	return "안녕히계세요 여러분~ 전 이 세상의 모든 굴레와 속박을 벗어던지고 제 행복을 찾아 떠납니다";
    }
}

 

수료자 B

public class KR extends HangHaeGraduate {
    @Override
    public String getPostScript() {
    	return "더러웠고 다신 보지 말자";
    }
}

 

수료자 C

public class SY extends HangHaeGraduate {
    @Override
    public String getPostScript() {
    	return "@#$ㅃ#$ㅃ$%^ㄸㄲㅉ!@@#@$!";
    }
}

 

  • 수료식 관한 클래스들 (팩토리 클래스)

수료식 예정 

public abstract class HangHaeFinal {
	abstract HangHaeGraduate graduate(HangHaeGraduate hhg);
}
public abstract class HangHaeFinal10th extends HangHaeFinal {
    @Override
    abstract HangHaeGraduate graduate(String graduateName ){
    	swich(graduateName) {
            case "KR" :
            	return new KR();
            case "ES" :
                return new ES();
            case "SY" :
                return new SY();
        }
    };
}

 

  • Main
public class Main {
	public static void main(String[] args){
    	HangHaeFinal hhf = new HangHaeFinal();
        
        String[] graduateList = {"KR","ES"};
        
        for(String graduate : graduateList){
            HangHaeGraduate graduateOne = hhf.graduate(graduate);
            system.out.println(graduateOne.getPostScript());
        } 
    }
}

// 더러웠고 다신 보지말자
// 안녕히계세요 여러분~ 전 이 세상의 굴레와 속박을 벗어던지고 제 행복을 찾아 떠납니다

 

 

 

 

 

팩토리 메소드 패턴(Factory Method Pattern)

1. 팩토리 메소드 패턴이란? 다양한 구현체 (Product)가 있고, 그 중에서 특정한 구현체를 만들 수 있는 다양한 팩토리(Creator)를 제공할 수 있다. 1-1. 정의(Definition) 1-1-1. 팩토리 패턴? 객체의 생성을

dev-youngjun.tistory.com

 

'Computer Science > Design Pattern' 카테고리의 다른 글

CQRS 패턴  (0) 2023.02.07

+ Recent posts