1. 서론
1.1. 들어가기에 앞서
•
이 컨텐츠는 CQRS, DDD, graphql을 함께 사용하여 확장 가능하고 유지보수 가능한 엔터프라이즈 애플리케이션을 구축하는 방법을 설명
•
graphql은 비즈니스 모델을 풍부하게 노출하는 얇은 계층 역할을 하며, CQRS는 읽기/쓰기 모델을 분리하여 효율적인 데이터 처리를 가능하게 하고, DDD는 복잡한 비즈니스 도메인을 소프트웨어 모델에 효과적으로 반영하도록 도움
•
이 세 가지 기술의 조합은 특히 대규모 엔터프라이즈 환경에서 클라이언트에게 통합된 비즈니스 모델을 제공하고 스키마 변경에 대한 안정성을 보장하는 데 강력한 시너지를 발휘
1.2. 각 개념의 핵심 역할
•
클린 아키텍처
◦
애플리케이션을 도메인, 애플리케이션, 인프라, 프레젠테이션 계층으로 분리하여 외부 구성 요소로부터의 독립성과 높은 모듈성을 제공
•
CQRS (Command Query Responsibility Segregation)
◦
시스템의 읽기(쿼리)와 쓰기(커맨드) 모델을 분리하여 각 작업에 최적화된 처리를 가능하게 함
•
DDD (Domain-Driven Design)
◦
소프트웨어 모델을 비즈니스 요구사항과 일치시키고, 개발자와 도메인 전문가 간의 공유 언어(유비쿼터스 언어)를 사용하여 복잡한 비즈니스 문제를 해결하는 데 중점을 둠
2. GraphQL의 설계 의도와 엔터프라이즈 애플리케이션의 정의
2.1. GraphQL의 기본 개념과 설계 의도
•
Facebook(현재 Meta)가 만든 GraphQL은 비즈니스 모델을 노출하는 얇은 계층으로, REST보다 더 풍부한 데이터를 제공하는 것이 목적임.
•
GraphQL의 핵심은 세 가지 타입: 쿼리(읽기), 뮤테이션(쓰기/변경), 구독(이벤트 알림)임.
•
쿼리는 부작용 없는 읽기, 뮤테이션은 데이터 변경, 구독은 이벤트에 대한 실시간 알림을 담당함.
2.2. 엔터프라이즈 애플리케이션과 그 구조
•
엔터프라이즈 애플리케이션은 핵심 비즈니스 프로세스, 판매, 회계, 인사 등 모든 운영 단계를 통합하는 시스템임.
•
복잡한 시스템을 효율적으로 관리하기 위해 '클린 아키텍처', ' CQRS', '도메인 주도 설계(DDD)'라는 세 가지 패턴이 사용됨.
•
클린 아키텍처는 관심사의 분리와 모듈화, 외부 시스템(데이터베이스, 프레임워크 등)와의 독립성을 강조함.
•
계층 구조는 도메인, 애플리케이션, 인프라스트럭처, 프레젠테이션(그래프QL 포함)으로 나뉘며, 외부에서 내부를 참조하는 방향으로 설계됨.
3. 클린 아키텍처의 이해
3.1. 클린 아키텍처의 목적
•
관심사의 분리(separation of concerns)를 추구하고, 애플리케이션에 명확한 계층화를 도입하여 높은 모듈성을 제공하는 아키텍처 스타일.
•
외부 구성 요소(프레임워크, 데이터베이스 시스템 등)로부터 독립성을 확보하기 위해 사용됨.
•
기술적인 요소들은 빠르게 변화하므로, 애플리케이션 수명 주기 동안 교체할 수 있도록 독립성을 유지하는 것이 중요.
3.2. 클린 아키텍처의 계층 구조
•
도메인 계층: 핵심 비즈니스 모델(엔티티, 리포지토리 등)을 포함하며 변경이 적음.
•
애플리케이션 계층: 유스케이스를 조율하며, 도메인과 상호작용하는 역할.
•
인프라스트럭처 계층: 데이터베이스, 메시징 시스템 등 기술적 요소를 담당하며 교체 가능.
•
프레젠테이션 계층: 사용자 인터페이스 또는 API( GraphQL등)가 위치하며, 외부 계층이 내부를 참조함.
4. CQRS와 GraphQL과의 연계
4.1 CQRS의 기본 개념 및 역할
•
CQRS(Command Query Responsibility Segregation)는 명령(쓰기)과 조회(읽기) 모델을 분리하는 개념.
•
이 패턴은 애플리케이션 계층에서 비즈니스 사례나 유스케이스를 정의하는 데 사용됨
•
CQRS를 통해 애플리케이션은 웹샵의 결제와 같은 작업 기반(task-based) 상호작용을 수행할 수 있음
◦
이는 도메인 계층과의 상호작용을 완전히 설명하는 하나의 명령으로 설계될 수 있음
4.2 CQRS와 GraphQL의 연관성
•
CQRS에서 명령(Commands)은 시스템에 부작용을 일으키는 GraphQL의 뮤테이션(Mutations)에 해당됨.
•
GraphQL은 CQRS와 동일한 추상화를 가지며, 부작용이 없는 읽기 모델인 쿼리(Queries)와 명령에 밀접하게 관련된 뮤테이션을 포함함.
•
뮤테이션을 수행하면 시스템에 미치는 영향을 즉시 읽을 수 있어, 클라이언트 애플리케이션이 서버에 추가 왕복(round trip) 없이 변경 사항을 읽고 클라이언트 저장소를 업데이트하기에 좋음.
4.3 CQRS와 GraphQL에서의 최종 일관성
•
CQRS는 최종 일관성(eventual consistency)을 수용함.
◦
명령이 읽기 모델을 즉시 업데이트하고 변경 효과를 반환할 수도 있지만, 업데이트를 지연시킬 수도 있음.
◦
예를 들어, 더 많은 비용이 드는 비동기 사용자 흐름은 완료하는 데 시간이 걸릴 수 있으며, 이 경우 최종 일관성을 사용하여 읽기 모델을 업데이트함.
•
GraphQL의 모범 사례는 변경 사항을 즉시 반환하는 것이지만, 다른 패턴도 존재함
◦
뮤테이션 후 변경 사항 대신 트랜잭션 ID를 반환하고, 이벤트 구독을 통해 업데이트를 받을 수 있음
▪
뮤테이션을 먼저 수행한 다음 구독해야 하므로 두 개의 연결이 필요.
▪
이는 전송 계층에서 더 많은 작업을 요구.
•
최종 일관성은 UI부터 비즈니스 흐름까지 시스템에 복잡성을 더하므로, 필요한 경우에만 사용하는 것이 중요.
◦
GraphQL쿼리는 CQS의 쿼리와 100% 일치하며, 읽기 모델에서 작동.
5. 도메인 주도 설계(DDD)의 핵심 개념과 GraphQL의 역할
5.1. DDD의 목적 및 유비쿼터스 언어
•
DDD의 정의 및 적용 대상
◦
DDD는 복잡한 애플리케이션을 위한 전략적 설계 접근 방식.
◦
클린 아키텍처, CQRS, DDD를 함께 사용하는 시스템은 장기적으로 유지보수 가능하며, 시간이 지남에 따라 구성 요소를 교체할 수 있도록 구축.
•
DDD의 핵심 목표: 유비쿼터스 언어(Ubiquitous Language)
◦
DDD는 소프트웨어 모델을 비즈니스 요구사항과 일치시키고, 개발자와 도메인 전문가 간의 공유 언어인 "유비쿼터스 언어"를 사용하여 핵심 비즈니스 문제에 집중하도록 도움.
◦
이 언어는 개발자와 이해관계자가 함께 비즈니스 작동 방식과 소프트웨어로의 전환에 대한 공통 이해를 구축하는 데 사용.
◦
비즈니스를 이해하는 것이 DDD를 프로젝트에 적용하고 성공적인 소프트웨어 애플리케이션을 작성하는 데 핵심.
◦
DDD의 목표는 복잡한 시스템을 단순화하고 이해하기 쉽고 유지보수 가능하게 만드는 것.
5.2. GraphQL 스키마와 DDD의 유비쿼터스 언어
•
GraphQL 스키마의 역할
◦
GraphQL에서도 유비쿼터스 언어는 스키마
◦
이는 비즈니스 모델을 사용자에게 노출하는 방식
•
풍부한 스키마를 통한 비즈니스 모델 표현
◦
GraphQL 스키마는 비즈니스의 모든 측면을 설명하는 풍부한 정보를 담고 있음
▪
예를 들어, removeProductStock 뮤테이션을 통해 제품 재고를 제거하는 비즈니스 로직을 표현할 수 있음
◦
뮤테이션 실행 시 반환되는 제품 정보나 오류를 통해 어떤 도메인 오류가 발생할 수 있는지 정확히 확인할 수 있음
◦
이러한 뮤테이션을 작성하고 비즈니스 모델에 대해 추론하는 것이 매우 유용
•
스키마 변경에 대한 유연한 대응
◦
미래에 모델이 변경되어 새로운 예외나 도메인 오류가 도입될 경우에도 GraphQL의 뮤테이션 컨벤션 패턴을 통해 알려지지 않은 오류를 처리할 수 있음
5.3. 애그리게이트와 바운디드 컨텍스트(경계 컨텍스트)
•
애그리게이트는 관련 엔티티와 값 객체를 하나의 단위로 묶은 것임.
•
바운디드 컨텍스트는 특정 비즈니스 영역(예: 주문, 상품, 사용자)을 독립적으로 설계하는 개념임.
•
GraphQL에서는 각각의 바운디드 컨텍스트를 서브그래프로 구현하고, 이를 통합하는 '통합 스키마'로 관리함.
•
여러 바운디드 컨텍스트(서브그래프)는 각각 독립된 GraphQL서버 또는 스키마임.
◦
이들을 하나의 '통합 스키마'로 묶는 게이트웨이(Orchestrator)가 있으며, Netflix는 4,000개 서브그래프를 운영하며 초당 600만 요청을 처리함.
◦
클라이언트는 단일 통합 스키마를 통해 여러 비즈니스 영역에 접근하며, 내부 구현은 숨김.
6. DDDD의 이벤트와 GraphQL Subscription
6.1. DDD의 이벤트 종류
•
도메인 이벤트(Domain Events)
◦
애그리거트(aggregate)와 엔티티(entity) 사이, 즉 바운디드 컨텍스트 내에서 발생하는 이벤트
◦
이를 통해 엔티티를 격리하여 설계하고, 유스케이스 내에서 함께 상호작용하도록 조율할 수 있음
•
통합 이벤트(Integration Events)
◦
다른 바운디드 컨텍스트의 엔티티 간에 발생하는 이벤트
◦
메시징 시스템을 통해 설계될 수 있으며, 도메인 이벤트를 통합 이벤트로 변환하여 엔티티들이 상호작용하도록 함
6.2. GraphQL의 이벤트 시스템: 구독(Subscriptions)
•
GraphQL에도 이벤트 시스템이 있지만, 이는 주로 외부 세계, 즉 GraphQL 엔드포인트의 소비자들을 위한 것
•
"구독"이라고 불리며, 사용자가 구독하여 시스템에 변경이 발생할 때 쿼리를 실행하고 클라이언트 스토어를 업데이트하는 등 상호작용적인 경험을 제공함
7. DDD 도입의 장점과 고려사항
7.1. DDD 도입의 장점
•
소프트웨어와 비즈니스 요구사항 간의 정렬
◦
소프트웨어 엔지니어들이 비즈니스 도메인을 깊이 이해하게 되며, 이해관계자와 개발자가 함께 시스템을 구축하여 시스템에 대한 더 깊은 이해를 얻음
•
유연성
◦
구성 요소를 교체하거나 새로운 구성 요소를 도입할 수 있는 유연성을 제공
•
확장성
◦
초당 6백만 건의 요청을 처리하는 시스템과 같은 대규모 확장성을 달성할 수 있는 기반을 제공
◦
모든 어려운 결정을 지금 당장 내릴 필요는 없지만, DDD와 같은 패턴과 구조적 접근 방식은 필요할 때 시스템을 해당 수준으로 확장할 수 있게 해줌
7.2. DDD 도입의 문제점 및 고려사항
•
복잡성
◦
적절한 DDD를 수행하려면 비즈니스를 깊이 이해하고, 여러 사람을 모아 비즈니스에 대한 공통 이해를 구축해야 함
◦
그러나 엔지니어들이 비즈니스를 이해하는 것은 바람직한 문제임
•
시간 소모
◦
새로운 프로젝트를 시작하거나 기존 프로젝트를 DDD 방식으로 리팩토링할 때 높은 초기 투자가 필요함
◦
개발자들의 기술을 향상시키는 데도 시간이 걸리지만, 그만한 가치가 있음
8. GraphQL을 통한 엔터프라이즈 애플리케이션의 실현
8.1. 비즈니스 계층 위의 얇은 계층
•
GraphQL의 핵심 역할: 비즈니스 계층 위의 얇은 계층
•
GraphQL은 비즈니스 계층 위에 얇은 계층으로 설계됨
1.
제품 작업(product operations)과 같은 GraphQL 리졸버(resolver)는 매우 얇은 함수 또는 메서드로, 비즈니스 로직을 직접 포함하지 않음
◦
리졸버는 단순히 미디에이터(mediator)를 주입하고 실행하여 결과를 반환함
•
쿼리 코드 또한 매우 간단하며, 데이터 요청 방식만 기술하고 실제 데이터 획득 구현(프레임워크 등)은 인프라스트럭처 계층의 데이터 로더(data loader)에 위임
◦
데이터 로더는 데이터베이스에서 데이터를 가져오는 방법을 정의하며, 매핑, 정렬, 프로젝션, 페이징 등의 구현이 포함
◦
이러한 구현은 교체 가능하며, 코드가 작고 이해하기 쉬워 추론하기 용이
8.2. 느슨한 결합 및 효율적인 데이터 페칭
•
GraphQL은 도메인 모델 간의 느슨한 결합을 가능하게 함
◦
예를 들어, 브랜드와 제품 간의 관계는 도메인 계층에서 브랜드 ID를 통해 느슨하게 연결됨
•
데이터 로더를 통한 관계 해결
◦
GraphQL 타입에서 브랜드 타입을 확장하고, products와 같은 추가 필드를 제공하여 다른 엔티티를 통합할 수 있음
◦
이때 리졸버는 다시 미디에이터를 사용하여 브랜드별 제품을 가져오는 명령을 보냄
◦
리졸버는 작동에 필요한 최소한의 데이터(예: 브랜드 ID)를 정의하고, 이를 통해 프로젝션(projection)을 수행할 수 있음
◦
이러한 코드는 작고 반복적일 수 있지만, GraphQL 계층 없이도 독립적으로 테스트할 수 있어 유지보수성이 높음
◦
인프라스트럭처 계층의 데이터 로더는 복잡한 배치(batching) 및 페이지네이션(pagination) 로직을 처리하여, 여러 브랜드 ID에 대한 제품 목록을 한 번에 가져오는 등 효율적인 데이터베이스 쿼리를 가능하게 함
•
간단한 GraphQL 계층
◦
GraphQL 계층은 매우 평면적이고 간단하며, 대부분의 로직은 읽기 모델에서 추론되어 작동
8.3. 스키마 진화와 버전 관리
•
단일 스키마 접근
◦
GraphQL의 복합 스키마는 모든 바운디드 컨텍스트를 하나의 스키마로 통합하여 모든 클라이언트가 접근할 수 있도록 함
•
클라이언트 파손 방지
◦
GraphQL은 스키마 진화(schema evolution) 스토리가 REST보다 훨씬 뛰어나 클라이언트가 깨지는 것을 방지
•
스키마 레지스트리(Schema Registry)
◦
Nitro와 같은 스키마 레지스트리는 게이트웨이와 하위 서비스(바운디드 컨텍스트)를 관리하고, GraphQL 서버를 사용하는 클라이언트들을 추적
◦
개발 시 클라이언트는 쿼리를 작성하고 필요한 데이터를 정의
◦
프로덕션 배포 시 빌드 프로세스는 클라이언트에서 쿼리를 제거하고, 이를 해시(hash)로 대체하여 스키마 및 클라이언트 레지스트리에 업로드
◦
이후 애플리케이션은 REST 애플리케이션처럼 작동
◦
GraphQL 서버는 클라이언트가 보낸 암호화된 해시를 스키마 레지스트리에 질의하여 해당 쿼리 정보를 얻고 처리
9. 결론 및 요약
•
GraphQL은 비즈니스 모델을 명확히 표현하고, 유연성과 확장성을 갖춘 엔터프라이즈 애플리케이션개발에 적합함.
•
DDD, CQRS, 클린 아키텍처와 결합하면, 유지보수성과 확장성을 높이면서도, 클라이언트와 서버 간의 안정적인 통신이 가능함.