티스토리 뷰
개요
# 참고
IMPLEMENTING DOMAIN DRIVEN DESIGN (도메인 주도 설계 구현) - 반 버논 지음
# 개요
DDD에 대한 기술서적을 읽고 DDD가 무엇인지 이해하고 어떻게 코드에 적용시킬 수 있는지 고민해 보려고 한다.
# 4장의 로드맵
- DIP와 헥사고날 아키텍처를 통해 신뢰할 수 있는 계층 아키텍처를 개선하는 방법을 배우자
- 헥사고날이 서비스 지향과 REST를 지원하는 방법을 살펴보자
- 개인 데이터 패브릭이나 그리드 기반 분산 캐시의 관점과 이벤트 주도 스타일의 관점을 배우자
- CQRS라는 새로운 아키텍처 패터이 어떻게 DDD에 도움이 될지 생각해보자
4장. 아키텍처
DDD의 가장 큰 장점 중 하난는 특정 아키텍처의 사용을 요구하지 않는다는 점입니다. 신중하게 만든 핵심 도메인이 바운디드 컨텍스트의 중심에 머무르기 때문에 하나 이상의 아키텍처적 영향력이 전체 애플리케이션이나 전체 시스템에 영향을 미칠 수 있습니다. 아키텍처 스타일을 올바르게 사용하는 것도 중요하지만 너무 남용하지 않는 것도 중요합니다.
4-1. 올바른 아키텍처의 선택
- 중앙 데이터베이스에 정보를 저장시키는 데스크톤 애플리케이션을 설계하기 위해 계층 아키텍처를 선택
- 유닛과 기능 테스트 도구를 도입해 품질을 관리할 필요가 있어 DIP를 도입
- 애그리게잇 패턴과 리파지토리를 사용했기 때문에, 리파지토리 인터페이스의 뒤편에서 인메모리 영속성을 준비하고 개발
- 헥사고날 아키텍처의 포트와 어댑터 접근법으로 새로운 클라이언트를 추가가 용이
- NoSQL, 메시징 기능, 클라우드 도입
- CQRS 아키텍처 패턴의 도입으로 이점 추구
- 이벤트 기반의 아키텍처를 도입하고, 파이프와 필터 패턴을 활용
4-2. 의존성 역행의 원리
의존성이 영향을 주는 방식을 조정함으로써 전통적인 계층 아키텍처를 개선하는 방법이 있다. 의존성 역행 이란 상위 수준의 모듈은 하위 수준 모듈에 의존해서는 안 되며, 둘 모두 추상화에 의존해야 한다는 말이다. DIP는 개발자의 필수적인 개념이라고 생각하여 긴 설명은 생략하도록 하겠습니다. 전통적인 계층 아키텍처는 각 계층(인프라, 도메인, 서비스, 애플리케이션)이 서로 느슨하게 연결되어 있지만 서로의 계층을 직접적으로 참조할 수 없다는 의미이다.
4-3. 헥사고날 아키텍처
헥사고날 아키텍처는 다양한 이질적 클라이언트가 동등한 지위에서 시스템과 상호작용하도록 할 수 있게 해준다. 즉, 새로운 클라이언트가 필요하다면 API가 클라이언트의 입력을 이해하도도록 변환해주는 어댑터만 추가하면 된다. 따러서 그래픽, 영속성, 메시징과 같은 출력 메커니즘이 다양해지고 쉽게 대체할 수 있게 된다.
계층 아키텍처를 사용하고 있다면 실제로 많은 팀에서 헥사고날을 사용하고 있을 수 있다. 의존성 주입이 무조건 헥사고날을 의미하지 않으며, 아키텍처를 만드는 과정에서 자연스럽게 포트와 어댑터 스타일의 방향으로 흘러가게 해줄 뿐이다.
헥사고날 아키텍처는 프론트영역과 백엔드영역으로 구분 하기 보다는 외부와 내부 두 영역을 주로 둔다. 외부영역은 일질적인 클라이언트가 입려을 보낼 수 있도록 해주며, 영속 데이터를 가져오거나 데이터베이스, 메시징 메커니즘을 제공한다.
즉, 헥사고날 아키텍처의 핵심은 포트와 어댑터의 개념을 기반으로 어떠한 클라이언트가 입려되더라도 내부는 크게 영향을 받으면 안된다는 것이다. 또한 내부로 갈 수록 의존성을 낮아져 헥사고날 내부 정보가 외부로 새어나가지 않게 해야 한다.
헥사고날 아키텍처는 다양한 측면에서 활용되므로, 시스템에 필요한 여러 아키텍처를 구현할때 든든한 기반이 된다. 서비스 지향이나 Rest, 이벤트 주도 아키텍처, CQRS, 데이터 패브릭, 그리드 기반 분산 캐시, 맵-리듀스 분산 병렬 처리를 사용할 수 있다. 헥사고날 아키텍처는 이런 부가적인 아키텍처를 지원하는 강력한 기반을 형성한다.
4-4. 서비스 지향 아키텍처 (SOA)
서비스 지향 아키텍처를 정의하는 것은 사람마다 차이가 있다. 따라서 몇 가지 공통점을 찾아 정의하는 것이 최선이다.
# 서비스 설계의 원칙
서비스의 느슨한 결합 : 서비스는 의존성을 최소화하고 오직 서로에 대해서만 알고 있다.
서비스 추상화 : 서비스는 그드르이 계약만을 게시하고, 클라이언트로부터 내부 로직을 숨긴다.
서비스 재사용성 : 서비스는 더 대단위 서비스를 만들기 위해 다른 상대에게 재사용될 수 있다.
우리는 서비스 경계와 도메인 모델을 핵심에 두고, 설계 원리를 헥사고날 아키텍처와 결합할 수 있다. 소비자는 REST와 SOAP와 메시징을 이용해 서비스에 접근한다. 하나의 헥사고날 기반 시스템이 다수의 기술적 서비스 엔드포인트를 지원한다는 점에서 DDD가 SOA에서 어떻게 사용이 되는지와 관련있다.
이 집합은 저체 비즈니스 서비스의 일부일 뿐이다. SOA 해결책 공간에선 헥사고날 아키텍처를 사용하는지 여부를 떠나서, 다양한 바운디드 컨텍스트가 나타나게 된다. SOA나 DDD 모두는 각 기술적 서비스 집합마다 설계하고 배치하는 방법을 명시할 필요가 없으며, 이미 사용할 수 있는 다양한 선택지가 존재한다.
결론적으로 DDD를 사용할 때의 목표는 언어적으로 잘 정의된 완벽한 도메인 모델을 바탕으로 바운디드 컨텍스트를 생성하는 것이다. 아키텍처가 도메인 모델의 크기에 영향을 주지 않아야 하며, REST 리소스나 SOAP 인터페이스나 시스템 메시지 타입과 같은 하나 이상의 기술적 서비스 엔드포인트를 사용해 바운디드 컨텍스트의 크기를 나타내려 한다면 이러한 상황에 빠질 수도 있다. 이런 식의 접근은 매우 작은 바운디드 컨텍스트와 도메인 모델이 만드어지고, 각각에선 단 하나의 엔티티가 작은 단일 애그리게잇 루트의 역활을 수행하는 상황에 빠지게 만든다. 이렇게 되면 단일 엔터프라이즈 내에 이런 바운디드 컨텍스트 미니어처가 수없이 존재할 것이다.
4-5. REST: 표현 상태 전송
REST는 지난 수년 동안 가장 많이 사용된 동시에 남용되기도 한, 아키텍처의 화제중 하나가 되었다. 약자를 사용하기 시작되며 사람들 저마다 생각이 달라지게 되었다. 일부 사람들은 REST가 SOAP를 사용하지 않고 HTTP 연결상에서 XML을 전송한다고 생각하며 일부 사람들은 HTTP와 JSON의 사용을 같은 의미로 생각하기도 한다.
# 아키텍처 스타일로서의 REST
REST를 위해 가장 먼저 필요한 부분은 아키텍처 스타일이라는 개념에 관한 이해이다. 아키텍처 스타일이란 특정 설계를 위한 설계 패턴이 무엇인지에 대한 구조적인 그림이다. 일반적으로는 사용되는 측면을 추상화하며, 이를 통해 기술적 세부사하에만 집착하다가 길을 잃지 않게 해주고 어떤 부분에 이점이 있는지 파악할 수 있다.
Q. 그렇다면 우리는 왜 REST로 웹서비스를 만들게 되었을까?
REST는 웹 픝로토콜을 여러 가지 방법으로 활용할 수 있다는 점을 발견했기 떄문이다. 웹 프로토콜은 원래 의도에 맞게 사용이 될 수도 있지만, 그 의도와는 다른 방법으로도 사용될 수 있다. 우리가 값 객체를 저장할때 RDBMS보다 NoSQL/키-값 저장소가 더 적합한 것 처럼 다른 종류의 분산 시스템 아키텍처가 더 알맞을 수도 있다.
# Restful HTTP 서버의 주요 특징
Q. 레스트풀 HTTP를 사용한 분산 아키텍처의 주요 특징은 무엇일까?
1. 리소스가 핵심 개념이다.
외부에서 접근 가능하도록 노츨하고 싶으 늬미 있는 대상이 무엇인지 결정하고, 각각에 구분된 식별자를 부연하다. 일반적으로 각 리소스는 하나의 URI를 가지는데, 각 URI는 반드시 하나의 리소스를 가리켜야 한다는 점이 더욱 중요하며, 이를 통해 외부로 노출한 대상을 각각 불러낼 수 있어야 한다. 리소스는 하나 이상의 포맷에 따라 상태를 판단할 수 있는 표현을 갖게 된다. XML이나 JSON, HTML폼과 같은 표현을 통해 클라이언트와 상호작용한다.
2. 자술적 메시지를 사용해 무상태로 의사소통한다.
HTTP가 무상태성, 비연결성을 가지는 것은 아마 많은 개발자 분들이 알고 있을 것이라고 생각합니다. 통신 이후 연결을 끊어 버리기 때문에 가볍고 확장성이 좋다는 장점을 가지고 있습니다. 따라서 클라이언트와 서버가 세션을 설정하기 위한 개별 요청에 의존하지 않는다는 점이 중요하며 다른 요청과는 독립적으로 각각의 리소스를 액세스할 수 있도록 해주어 대규모 확장성을 달성할 수 있다.
3. 레스트풀 HTTP의 메소드를 사용한다.
HTTP는 GET, PUT, POST, DELETE 동사를 가지고 있다. 대게는 GET과 POST를 많이 비교를 한다. GET은 안전한 오퍼레이션에만 사용이 되어야 하며, 클라이언트가 요청하지 않은 효과를 반영한 행동을 할 수 있으며, 잠재적으로 캐싱이 될 수 있다는 특징을 가진다.
4. 레스트풀 서버는 HATEOAS 사용이 가능하다.
레스트풀 서버는 하이퍼미디어라는 방버블 통해 클라리언트가 어플리케이션에서 일어날 수 있는 상태 변경에 맞는 경로를 찾아 줄 수 있도록 한다. 하이퍼미디어 즉, HATEOAS는 서버 입장에서 답을 줄때 연결 정보를 포함시켜서 클라이언트가 연결된 리소스와 상호작용할 수 있도록 해준다. 조금 더 풀어서 쓰면, 응답에 URI를 포함 시킬 수 있어 동적인 JSON 응답을 제공해 줄 수 있다는 의미이다.
# Restful HTTP 클라이언트의 주요 특징
지금까지 서버의 특징을 알아보았으니, 지금부터는 클라이언트의 특징을 알아보려고 한다.
1. HATEOAS 활용
클라이언트는 리소스 표현 내에 포함된 링크를 따라가거나 서버로 처리하 데이터를 보낸 후 결과로 돌아온 리소스로 리다이렉션함으로써 다른 리소스로 이동한다. URI에는 주소를 역참조하기 위해 필요한 모든 정보가 담겨 있기 때문에 호스트, 심지어 다른 기업에서 호스팅하는 리소스에도 말을 걸 수 있다. 이상적인 REST 설정에선 클라이언트가 단 하나의 잘 알려진 URI에서 시작해, 그 다음부턴 하이퍼미디어 제어를 계속 따라가게 된다.
4-6. REST와 DDD
도메인 모델을 레스트풀 HTTP로 바로 노출하는 것은 좋지 않다. 이런 접근법은 필요이상으로 취약한 시스템 인터페이스로 귀결되는데, 이는 도메인 모델 내의 변경 하나하나가 시스템 인터페이스로 바로 반영되기 때문이다. DDD와 레스트풀 HTTP를 방치는 방법에느 두가지 활용방법이 있다.
1. 시스템의 인터페이스에 별도의 바운디드 컨텍스트를 생성하고, 시스템의 인터페이스 모델에서 실제 핵심 도메인으로 액세스하기 위해 적절한 전략을 사용하는 것이다. 이 방법은 고전적인 방법이라고 여겨질 수도 있는데, 시스템의 인터페이스를 서비스나 원격 인터페이스가 아닌 단순한 리소스 추상화로 노출된 하나의 응집되 묶으로 바라보기 때무니다.
유비 쿼터스 언어를 포착하고 필요한 비즈니스 로직을 구현하면서도 인프라 세부사항의 대까 묻지 않은 순수한 도메인 모델을 설계하려 할 것이다. 신중하게 만들어진 이 도메인 모델에 인터페이스를 게시하기 위해, 레스트풀 시소스의 집합으로서 원격 인터페이스를 제공한다. 이 리소스는 클라이언트가 요구하는 유스케이스를 반영하는데, 이는 순수한 도메인 모델과는 거리가 멀 가능성이 크다. 하지만 각 리소스는 핵심 도메인 모델에 속한 하나 이상의 애그리게잇으로 부터 생성이 된다. 따라서 시스템의 인터페이스 모델에서 핵심 도메인을 분리하기 위해선 첫번째 접근법을 선호한다. 이를 통해 핵심 도메인에 변화를 준 후에 해당 변경이 시스템 인터페이스 모델에 반영돼야 하는지와 만약 그렇다면 어떤 매핑을 사용해야 최선일지 개별 케이스마다 결정된다.
이 접근법을 사용하면 주로 핵심 도메인의 클래스 위주로 시스템의 인터페이스 모델에 맞춰 클래스가 설계되지만, 정확히는 유스케이스가 이끌어 간다는 점이다.
2. 표준 미디어 타입을 강조하는 상황에선 다른 접근법을 사용하는 편이 좋다. 특정 미디어 타입이 단일 시스템 인터페이스뿐만 아니라 유사 클라이언트-서버 상호작용 유형도 함께 지원하기 위해 개발된다면, 각 표준 미디어 타입을 표현하기 위해 도메인 모델을 생성한다. 비록 일부 REST와 SOA의 지지자는 안티 패턴이라 볼지도 모르지만, 이렇게 만들어진 도메인 모델은 클라이언트와 서버를 모두 아우르며 재사용할 수도 있다.
이는 내부와 외부 모두를 좀 더 반영할 수 있는 크로스커팅 접근법이다. 이 포맷을 알아야 하는 모든 시스템은 이 모델을 사용할 수 있다. 당연하게도 이 접근법에선 서버가 여러 종류의 미디어 타입을 다뤄야 할 필용성이 발생하며, 여러 서버에서 같은 미디어 타입을 사용할 수도 있다.
디 두 접근법 중 무엇을 사용할진 시스템 설계자가 재사용의 측면에서 어떤 목표를 설정하는지에 따라 크게 좌우된다. 솔류션이 특화될수록 첫 번째 접근법이 더 유용해진다. 일반화 끝에는 공식 표준 본문을 통한 표준화가 이뤄진다면, 일반적으로 유용한 솔루션일수록 두 번째의 미디어 타입 중심의 접근법이 더 나을 것이다.
4-7. 왜 REST인가?
REST원리에 맞게 설계된 시스템은 느슨한 결합의 조건을 충족한다. 일반적으로 새로운 리소스를 추가해서 기존의 리소스 표현으로 연결하는 일은 매우 쉽다. 새로운 포맷의 지원이 필요한 시점에도 추가가 쉬우며, 이는 훨씬 덜 취약한 시스템 연결 집합을 이끌어낸다. 작은 리소스로 나뉘어지며, 각 단위가 개별적으로 테스트 및 디버깅할 수 있고, 사용가능한 진입 지점을 노출하기때문에 REST 기반 시스템은 이해하기가 훨씬 쉽다. 때문에 Restful HTTP는 느슨하게 결합돼야 하고, 높은 확장성이 필요한 아키텍처에게 훌륭한 선택이 될 수 있다.
CQRS
4-8. 커맨드-쿼리 책임 분리
사용자가 필요로 하는 데이터 뷰를 리파지토리로 쿼리하기란 어려울 수 있다. 특히 여러 애그리게잇 타입과 인스턴스로부터 데이터 뷰를 생성해야 할 때가 어렵다. 도메인이 정교할 수록 이러한 상황이 발생할 가능성이 높아진다.
리파지토리만을 사용하는 것은 바람직한 해결책이 아니다. 클라이언트가 여러 리파지토리를 통해 필요한 모든 애그리게잇 인스턴스를 취득한 후에 필요한 데이터만 DTO로 모아서 사용할도록 요구할 수 있다. 아니면 하나의 쿼리로 다양한 리파지토리에 흩어져 있는 데이터를 모아주는 특화된 파인더를 설계할 수도 있다. 또한 이런 해결책이 맞지 않다면 뷰가 엄격한게 모델의 애그리게잇 경계를 준수하도록 만들 수도 있다.
Q. 도메인 데이터를 뷰에 매핑하는 완전히 다른 방법이 있을까?
이에 대한 답이 커맨드-쿼리 책임 CQRS가 있다. 모든 메서드는 작업을 수행하는 커맨드이거나 데이터를 호출자에게 반환하는 쿼리중 하나여야 하며, 하나의 메소드가 두 역할을 모두 할 수는 없다. 즉, 질문하는 행동이 대답을 바꿔서는 안된다. 좀 더 형식적으로 말한다면, 메소드는 오직 참조적으로 투명해서 다른 부작용을 일으키지 않을 때만 값을 반환해야 한다.
1. 메소드가 객체의 상태를 수정한다면, 이 메소드는 커맨드이며 값을 반환하면 안 된다. 즉 void를 반환해야 한다.
2. 메소드가 값을 반환한다면, 이 메소드는 쿼리이며 직접적이든 간접적이든 객체 상태의 수정을 야기해선 안된다.
전통적으로 모델에서 찾게 되는 모든 순수 쿼리 책임을, 같은 모델의 순수 커맨드를 실행하는 책임으로부터 분리한다고 생각해보자. 어그리게잇은 쿼리 메소드 없이 오직 커맨드 메소드만을 포함하게 된다. 이 하나의 쿼리 메소드는 고유한 애그리게잇 식별자를 포함해 반환된다. 우리는 여전히 사용자에게 데이터를 보여줄 방법이 필요하다. 이를 위해선 최적화된 쿼리에 맞춰 두 번째 모델을 생성한다. 이를 쿼리 모델이라고 한다.
4-9. 클라이언트와 쿼리 처리기
클라이언트는 웹 브라우저 사용자나 사용자 지정 데스크톱 사용자 인터페이스일 수 있다. 쿼리 처리기는 SQL저장송와 같은 데이터베이스로의 기본 쿼리를 어떻게 실행하는지만을 알고 있는 간단한 컴포넌트를 나타낸다. 자바의 클라이언트는 데이터베이스로 직접 쿼리할 수도 있지만 이는 연결마다 하나의 데이터베이스 클라이언트를 요구하여 많은 수의 라이센스를 준비해야 할 수도 있다. 따라서 풀로 관리되는(커넥션 풀..?) 연결을 활용하는 쿼리 처리기를 사용하는 편이 최상의 선택이다.
데이터베이스 결과 집합을 사용하는 방법에는 두 가지 선택지가 있다.
1. 클라이언트가 결과 집합이나 해당 집합의 매우 기초적인 와이어 호환 직렬(XML, JSON)을 사용해야만 한다.
2. 다른 한가지는 DTO를 만들어서 클라이언트가 사용하도록 해야 한다는 관점이다.
# 쿼리 모델(읽기 모델)
쿼리 모델은 정규화되지 않은 데이터 모델이다. 이는 도메인 행동을 전달하기 위한 목적이 아니며, 오직 표시할 데이터만을 전달한다. 만약 이 데이터 모델이 SQL 데이터베이스라면, 각 테이블에는 한 가지 종류의 클라이언트 뷰만을 위한 데이터가 저장된다. 테이블 뷰는 테이블로 부터 생서되고, 각각의 테이블 뷰는 전체 정보의 논리적 하위 집합으로 사용된다. 이를 통해 사용자 타입에 따라 확인할 수 있는 데이터 보안을 추가한다. 일반 사용자의 뷰 컴포넌트는 일반 사용자 테이블 뷰에서 열을 선택한다. 매니저의 뷰 컴포넌트는 매니저의 테이블 뷰로부터 모든 열을 선택할 것이다. 이런 식으로 일반적인 사용자는 매니저가 볼 수 있는 데이터를 확인할 수 없게 된다. 데이터 모델의 설계는 가능한 많은 테이블이 애플리케이션의 보안 역할을 반영하는 가운데, 사용자 인터페이스 뷰 타입마다 하나의 테이블이 연결되는 패턴을 최대한 따라야 한다.
# 클라이언트가 커맨드 처리를 주도한다.
사용자 인터페이스 클라이언트는 커맨드 모델 내에 있는 애그리게잇의 행동을 실행 시키기 위한 방법으로 서버로 커맨드를 보내다. 보내진 커맨드는 실행할 행동의 이름과 이를 수행하기 위해 필요한 매개변수를 포함한다. 사용자 인터페이스는 커맨드를 정확히 매개변수화하는 데 필요한 데이터를 수집해야 한다. 이는 사용자 경허 설계에 많은 고민을 들여야 한다는 의미며, 명시적인 커맨드를 보낸다는 바람직한 목표를 향해 사용자를 인도해야 한다.
# 커맨드 처리기
캐맨드는 커맨드 핸들러/처리기가 수신하는데, 이들이 몇 가지 스타일 중 하나를 따를 수 있다.
1. 하나의 애플리케이션 서비스에서 여러 커맨드 핸들러와 함께 카테고리 스타일을 사용할 수 있따. 이 스타일은 커맨드의 카테고리에 맞춰 애플리케이션 서비스 인터페이스와 구현을 생성한다. 각 애플리케이션 서비스는 다수의 메소드를 가질 수 있고, 커맨드의 각 타입마다 캍테고리에 맞는 매개변수로 하나의 메소드를 선언한다. 이 스타일의 주요 이점은 단순성이다. 이런 종류의 핸들러는 이해가 쉽고 생성과 유지 관리가 쉽다.
2. 전용 스타일의 핸들러를 생성할 수 있다. 각 핸들러는 하나의 메소드를 갖고 있는 하나의 클래스다. 메소드 계약은 매개변수를 포함하고 있는 특정 커맨드의 사용을 용이하게 한다. 이 스타일에는 분명한 이점이 있다. 하나의 핸들러/처리기당 하나의 책임만 하고, 각 핸들러는 독립적으로 재사용될 수 있고, 핸들러 타입은 특정 종류의 커맨드의 양이 많을 때 이를 관리하기 위해 확장될 수 있다.
3. 이는 커맨드 핸들러의 메시지 스타일로 이어진다. 커맨드를 비동기식 메시지로 보내며, 이는 전용 스타일로 설계된 핸들러로 전달된다. 이는 각 커맨드 처리기 컴포넌트가 특정 타입의 메시지를 받도록 할 분만 아니라, 커맨드 프로세싱 부하를 처리할 수 있도록 주어진 타입의 처리기를 추가할 수 있다. 이 접근법에는 좀 더 복잡한 설계가 필요하기 때문에 이를 기본으로 사용하면 안된다.
대신 앞의 두 스타일 중 하나를 동기식 커맨드 처리기로 선택하고 확장성의 요구가 있을 때만 비동기식으로 변경하자. 또한 어떤 종류의 핸들러를 사용하든 각각을 모두 분리하자. 하나의 핸들러가 다른 핸들러에 의존하지 않도록 하자.
# 커맨드 모델(쓰기 모델)은 행동을 수행한다.
커맨드 모델상의 각 커맨드 메소드가 수행되면, 도메인 이벤트에서 설명하는 바와 같이 이벤트를 게시되면 수행이 완료된다. 가장 최근에 커맨드 모델에 일어난 변경으로 쿼리 모델을 업데이트할 때의 핵심이다. 이벤트 소싱을 사용한다면 막 수정된 애그리게잇의 상태를 저장할 때도 이벤트가 필수적이다. 그러나 이벤트를 CQRS와 함께 사용해야만 하는 것은 아니다. ORM이나 다른 접근법을 사용해 커맨드 모델을 데이터베이스에 저장할 수 있다.
# 이벤트 구독자가 쿼리 모델을 업데이트한다.
한 특별한 구독자가 커맨드 모델에 의해 게시된 모든 도메인 이벤트를 수신하도록 등록한다. 구독자는 각 도메인 이벤트를 사용해 쿼리 모델이 커맨드 모델에 일어난 가장 최근 변경을 반영하도록 업데이트 한다.
업데이트는 동기적으로 수행돼야 할까? 비동기적으로 수행돼야 할까? 이는 시스템의 일반적인 부하와 쿼리 모델 데이터베이스의 저장 위치에 달려 있다. 데이터 일관성 제약과 성능 요구사항이 결정에 영향을 주게 된다.
동기적으로 업데이트하기 위해선 일반적으로 쿼리 모델과 커맨드 모델이 같은 데이터베이스를 공유하며, 두 모델을 같은 트랜잭션으로 업데이트한다. 이를 통해 두 모델 사이에 일관성을 완벽하게 유지한다. 그러나 여러 테이블을 업데이트 하려면 더 많은 처리 시간이 필요하고, 이에 따라 서비스 수준 계약을 만족시키지 못할 수도 있다. 일반적으로 시스템이 큰 부하에 걸려 있고 쿼리 모델 업데이트 프로세스가 길다면 비동기적 업데이트를 사용하자. 이는 사용자 인터페이스가 커맨드 모델에 일어난 가장 최근 변경을 즉시 반영하지 못해서, 결과적으로 일관성을 갖는데 어려움이 있을 수 있다.지체 시간은 예측이 불가능하지만 다른 SLA를 만족시키기 위해선 불가피하게 감수해야 한다. ORM을 사용해 커맨드 모델을 저장한다면, 새로운 쿼리 모델 테이블을 채우기 위해 뒷받침하고 있는 커맨드 모델 저장소를 사용해 새 쿼리 모델 테이블을 채우자
# 결국은 일관성이 유지되는 쿼리 모델 다루기
만약 쿼리 모델의 일관성이 결국은 유지되도록 설계됐다면, 결과적으로 사용자 인터페이스 내에서 처리해야하는 이질성이 존재하게 된다. 예를 들면, 한 사용자가 커맨드를 보냈을 때, 다른 사용자 인터페이스 뷰에서도 쿼리 모델을 반영해 완전히 업데이트되고 일관성 있는 데이터를 가질 수 있을까? 이는 시스템 부하를 비롯한 여러 요소에 따라 달라진다. 그렇지만 일치하지 않는 다고 가정하고, 사용자 인터페이스가 절대로 일관성을 유지하지 못하는 최악의 상황에 대비해 설계하는 편이 낫다.
P.216 이후 생략
4-10 이벤트 주도 아키텍처
이벤트 주도 아키텍처는 이벤트의 생산,감지, 소비와 이벤트에 따른 응답 등을 촉진하는 소프트웨어 아키텍처다. EDA에 대한 개념이 부족한 사람들은 구글링을 통해 개념을 잡고 오자. 기본적인 EDA개념은 책에서 다뤄주지 않기에 어렵다.
헥사고날 아키텍처는 수신하고 송신하는 메시지를 통해 DEA에 참여하는 시스템의 개념을 표현할 수 있다. EDA가 헥사고날을 사용해야 하는 것은 아니지만, 개념을 표현하기에는 괜찮은 방법이다.
4-11. 이벤트 소싱
비즈니스 측에선 도메인 모델 내의 객체에서 일어나는 변경 추적에 대해 신경을 쓴다. 변경 추적에 관한 관심에는 여러 수준이 있고, 각 수준에 맞는 지원 방법이 있다. 일반적으로 비즈니스 측에선 엔티티가 생성된 시점과 마지막으로 수정된 시점과 누가 수정했는지만을 추적하기로 선택한다. 이런 개념을 하나의 엔티티에 적용하고, 이어서 하나의 애그리게잇으로, 그리고 모델 내의 모든 애그리게잇으로 적용을 이어간다고 생각해본다면, 모든 애그리게잇 인스턴스가 어떤 사건으로 인해 생성됐느닞 알 수 있게 해주고, 시간의 흐름에 따라 해당 애그리게잇 인스턴스에 어떤 일이 얼어났는지를 오퍼레이션별로 확인해주는 방법을 개발하고자 한다. 심지어 일시적 모델까지도 지원할 수 있따. 이런 수준의 변경 추적은 이벤트 소싱 패턴의 핵심이다.
4장 조금 더 마무리 하기..
'DDD' 카테고리의 다른 글
6장. 값 객체 / IDDD (도메인 주도 설계 구현) (0) | 2022.03.15 |
---|---|
5장. 엔티티 / IDDD (도메인 주도 설계 구현) (0) | 2022.03.14 |
3장. 컨텍스트 맵 / IDDD (도메인 주도 설계 구현) (0) | 2022.02.25 |
2장. 도메인, 서브도메인, 바운디드 컨텍스트 / IDDD (도메인 주도 설계 구현) (0) | 2022.02.20 |
1장. DDD를 시작하며 / IDDD (도메인 주도 설계 구현) (0) | 2022.02.19 |