티스토리 뷰
개요
# 참고
IMPLEMENTING DOMAIN DRIVEN DESIGN (도메인 주도 설계 구현) - 반 버논 지음
# 개요
DDD에 대한 기술서적을 읽고 DDD가 무엇인지 이해하고 어떻게 코드에 적용시킬 수 있는지 고민해 보려고 한다.
# 8장의 로드맵
- 도메인 이벤트가 무엇인지, 언제 그리고 왜 사용하는지 알아보자
- 이벤트가 객체로 모델링되는지, 언제 고유하게 식벼로대야 하는지 배우자
- 경량의 발행-구독 패턴을 알아보고, 클라이언트에 알림을 보내기 위해 이를 사용하는 방법을 살펴보자
- 이벤트를 발행시키는 컴포넌트와 구독하는 컴포넌트를 확인하자
- 이벤트 저장소를 개발해야 하는 이유와 개발하는 방법, 사용되는 위치를 생각해 보자
8장. 도메인 이벤트
도메인에서 발생한 사건을 포착하기 위해 도메인 이벤트를 사용하자. 이벤트는 아주 강력한 모델링 도구이다.
8-1. 언제 그리고 왜 도메인 이벤트를 사용할까?
이벤트를 외부의 서비스로 브로드 캐스트해야 할 때 주로 발생하는데, 엔터프라이즈 시스템은 결합에서 분리돼야 하고, 도메인 전반에 걸쳐 일어난 사건은 바운디드 컨텍스트 전체로 전달돼야 한다. 이런 이벤트가 발행되면 구독자가 알림을 받게 된다. 구독자가 이런 이벤트를 처리함으로써 로컬과 원격 바운디드 컨텍스트에 관범위한 영향을 미칠 수 있다.
이벤트는 로컬 시스템이든 외부 시스템이든 관심이 있는 대상으로 전달됐을 땐 보통 결과적 일관성을 위해 사용된다. 이는 두 단계 커밋의 필요성을 제거하고, 애그리게잇의 규칙을 지원한다. 애그리게잇의 규칙 중에는 트랜잭션에서 단 하나의 인스턴스만이 변경돼야 하고, 다른 모든 종속적 변경은 별도의 트랜잭션에서 일어나야 한다는 규칙이 있다. 따라서 로컬의 바운디드 컨텍스트의 다른 애그리게잇 인스턴스는 이 접근법을 통해 동기화할 수 있다. 이런 결합 분리는 높은 확장성과 협업하는 서비스 집합의 최고 성능을 제공하는데 도움이 된다. 또한 이를 통해 시스템 간의 느슨한 결합을 달성할 수 있다.
# 이벤트가 사용되는 이유
시스템은 배치 처리를 수행하는 평범한 상황을 생각해보자. 아마 시스템은 사용량이 적은 시간대에 일일 유지 관리를 처리할 텐데, 종종 이런 배치 처리의 수행에선 일부 복잡한 쿼리를 수행해 비즈니스 상황에 주목해야 할지 결정하는 일도 필요하다. 이를 처리하는 계산과 과정에는 큰 비용이 소모되고 모든 변경을 동기화하는데 많은 트래잭션이 필요하다.
만약 이 개별 사건을 각각 하나의 이벤트로 포착해서 시스템의 리스너에게 발행한다면, 일이 간단해지지 않을까? 실제로, 무슨 일이 언제 일어났는지 분명히 알 수 있어 그 결과로서 무슨 일이 일어나야 하느지에 관한 컨텍스트를 제공해주기 때문에 복잡한 쿼리를 제거할 수 있따. 따라서 수신한 각 이벤트의 알림에 맞춰 작업하기만 하면 된다.
Q. 모든 애그리게잇 커맨드가 이벤트를 발생시킬까?
이벤트의 필요성을 아는 만큼이나, 도메인의 불필요한 사건을 무시할 시점을 아는 일도 중요하다. 도메인 전문가가 직접 요구하는 수보다 이벤트가 더 많을 수도 있다. 이런 상황에선 이벤트 소싱이 사용한다.
8-2. 애그리게잇의 특성과 함께하기
클라이언트가 직접 요청한 내용을 바탕으로 이벤트가 생성되도록 설계하기도 한다. 이는 모델 내 애그리게잇 인스턴스상에 위치한 행동을 실행한 직접적인 결과가 아닌, 다른 일부 사건에 따른 응답으로써 이뤄진다. 시스템의 사용자가 그 고유한 권리를 사용해 이벤트로 간주되는 일부 활동을 초기화할 가능성도 있따. 이런 일이 일어나면 이벤트로 간주되는 일부 할동을 초기화할 가능성도 있따다. 이런 일이 일어나면 이벤트를 애그리게잇으로서 모델링할 수 있으며, 그 고유의 리파지토리로부터 취득할 수 있다.
이벤트는 여전히 불변하도록 설계되지만, 생성된 고유 식별자를 할당할 수도 있다. 그러나 이벤트의 여러 속성이 모여서 해당 식별자를 지원할 수도 있다. 속성의 집합에 의해 고유 식별자가 결정된다 하더라도, 엔티티에서 논의한 대로 생성된 고유 식별자를 할당하는 편이 최선이다. 이는 이벤트가 다른 모든 대상들 가운데 그 고유성을 잃을 위험 없이 다양한 설계 변경이 가능하도록 해준다.
이와 같은 방식으로 이벤트를 모델링하면 해당 리파지토리에 추가될 뿐만 아니라 메시징 인프라를 통해 발행할 수도 있다. 클라이언트는 이벤트를 리파지토리에 추가하고, 메시징 인프라를 통해 발생한다. 이런 접근법에선 리파지토리와 메시징 인프라 모두가 반드시 같은 영속성 인스턴스의 지원을 받거나, 아니면 두 커밋이 모두 성공함을 보장하기 위해 글로벌 트랜잭션이 필요하다.
8-3. 식별자
고유 식별자를 할당하는 이유를 분명히 하자. 이벤트를 서로 구분해야 할 때가 있지만, 그런 상황은 굉장히 드물다. 이벤트를 유발하고 생성해서 발행하는 위치인 바운디드 컨텍스트에선 이벤트를 서로 비교해야 할 이유가 거의없다. 그러나 혹시라도 이벤트를 비교해야 한다면 어떻게 해야 할까? 그리고 이벤트가 애그리게잇으로 설계됐다면 어떻게 해야 할까?
값 객체에서 그랬듯, 아마다 해당 속성으로써 이벤트 식별자를 표현해주면 충분하다. 이벤트의 이름/타입과 함꼐 이벤트를 유발시키는데 관여한 애그리게잇의 식별자와 이벤트가 발생한 타임스탬프를 통해 해당 이벤트를 충분히 구분해낼 수 있다.
하지만 이벤트가 애그리게잇으로 모델링된 상황에서나, 이벤트를 반드시 비교해야 하지만 그에 엮인 속성만으론 구분할 수 없는 상황에선 이벤트에 정형적인 고유 식별자를 할당할 수 있다. 또한 로컬 바운디드 컨텍스트의 밖에서 이벤트가 발행되면 메시지이 인프라 스트럭처가 이를 전달하기 위해 고유 식별자가 필요해진다.
# 메시지 오류
어떤 이유로 메시지의 재발송이 있어났든, 그 해결책은 원격 구독자로 하여금 중복 메시지 전달을 감지하고 이미 수신된 메시지를 무시하도록 하는 것이다. 이를 돕기 위해 일부 메시징 인프라는 고유 메시지 식별자를 해당 본문의 주변에 위치하는 헤더/이벨럽의 일부로 제공해, 모델이 이를 생성할 필요가 없도록 해준다. 어떤 경우든 원격 구독자는 메시지가 한 번 이상 전달되면 중복 제거를 관리하기 위해 고유 식별자를 활용할 수 있다.
8-4. 도메인 모델에서 이벤트를 발행하기
도메인 모델이 어떤 종류의 미들웨어 미시징 인프라로든 노출되지 않도록 하라. 이런 컴포넌트는 오직 인프라 내에만 위치한다. 그리고 도메인 모델이 이런 인프라를 간접적으로 사용할 땐 절대 명시적 결합을 형성하지 않는다.
도메인 이벤트를 도메인 모델 외부의 컴포넌트와 결합하지 않고 발행하는 가장 간단하고 효과적인 방법은 경량 옵저버의 생성이다. 이는 옵저버와 Pub-Sub와 같은 패턴을 나타내기 위해 사용했다. 이 패턴을 사용하면 가볍고 간결하다는 특징이 있다. 이는 이벤트가 발행할 때 네트워크를 통과하지 않기 때문이다. 등록된 모든 구독자는 발행자와 같은 프로세스 공간의 같은 스레드에 실행된다. 이벤트가 발행되면, 그에 해당하는 구독자를 하나씩 선택해 동기식으로 알림으 보낸다. 이는 모든 구독자가 같은 트랜잭션 내에서 실행된다는 의미를 내포하고 있는데, 아마도 도메인 모델을 직접 사용하는 애플리케이션 서비스가 이를 제어한다.
# 구독자
구독자는 또 다른 애그리게잇 인스턴스를 가져와서 변경을 유발하는 커맨드 행동을 수행해선 안 된다. 중요한 규칙인 단일 트랜잭션당 단일 애그리게잇 인스턴스 수정의 원칙을 침해한다. 단일 트랜잭션에 사용되는 애그리게잇 인스턴스를 제외한 다른 모든 애그리게잇 인스턴스는 비동기적 수단을 통해 일관성을 강제해야 한다.
메시징 인프라를 통해 이벤트를 전달하면 비동기적으로 대역 외 구독자에게 보낼수 있다. 같은 바운디드 컨텍스트에 있을 수도 있고, 다른 컨텍스트에 있을 수도 있다. 다른 외부 서브도메인에 속한 임의의 수의 바운디드 컨텍스트로으 ㅣ이벤트 발행은 도메인 이벤트라는 용어에서 도메인이라는 단어를 강조한다. 다시 말하자면, 이벤트는 모데인을 아우르는 개념이지 하나의 바운디드 컨텍스트만의 개념이 아니다. 이벤트 발행의 계약은 적어도 엔터프라이즈 범위나 그보다 더 넓은 범위에 속해야 한다. 넓은 브로드캐스트라고 해서 같은 바운디드 컨텍스트 내의 소비자가 이벤트 전달하지 못하도록 금지하진 않는다.
8-5. 뉴스를 원격 바운디드 컨텍스트로 전파하기
바운디드 컨텍스트 사이에서 사용하는 모든 메시징 메커니즘을 사용하기 위해선 일관성을 달성하려는 의지가 필요하다. 하나 이상의 다른 모델이 변경되도록 영향을 미치는 어떤 모델의 변경은 일정 시간이 흐르기 전에는 완전한 일관성을 달성하지 못한다. 더 중요한 점은 해당 시스템이 다른 시스템에 미치는 영향에 따라 시스템 전체가 완전한 일관성을 달성하지 못하는 상황이 발생할 수도 ㅇ있다는 점이다.
8-6. 메시징 인프라의 일관성
메시징 솔루션에서 적어도 두 가지 메커니즘은 반드시 항상 서로 일관성을 유지해야 한다는 점이 놀라울 수 있다. 그 두 가지는 도메인 모델이 사용하는 영속성 저장소, 모델이 발행한 이벤트를 전달하기 위해 사용하는 메시징 인프라의 영속성 저장소다.
Q. 어떻게 모델과 이벤트 영속성의 일관성을 달성할 수 있을까?
1. 도메인 모델과 메시징 인프라가 같은 영속성 저장소를 공유한다. 이는 모델의 변경과 새로운 메시지의 삽입이 같은 로컬 트랜잭션하에 커밋되도록 해준다. 여기엔 성능이 상대적으로 좋다는 장점이 있다. 메시징 시스템의 저장 공간이 반드시 모델과 같은 데이터베이스에 위치해야 한다는 단점이 있어, 이는 선호의 문제점 이며, 모델 저장소와 메시징 메커니즘 저장소가 서로 공유될 수 없다는 가능한 옵션이 아니다.
2. 도메인 모델의 영속성 저장소와 메시징 영속성 저장소가 글로벌 XA 트랜잭션하에 제어된다. 이는 모델과 메시징 저장소를 서로 분리할 수 있다는 장점이 있다. 그러나 글로벌 트랜잭션을 위한 특별한 지원이 필요하다는 단점이 있어서, 어떤 영속성 저장소나 메시징 시스템에서든 모두 가능하진 않다. 글로벌 트랜잭션은 고비용이고 성능이 나쁜 경향이 있다. 또한 모델의 저장소나 메시징 메커니즘의 저장소 중 하난 혹은 둘 모두가 XA와 호환되지 않을 수 있다.
3. 도메인 모델을 저장하기 위해 사용하는 영속성 저장소에 이벤트를 위한 특별한 저장 영역을 생성한다. 이를 이벤트 저장소라고 하는데 이 저장 영역은 옵션1과 비슷하지만, 메시징 메커니즘이 아닌 바운디드 컨텍스트가 소유하고 제어한다. 생성한 대역 외 컴포넌트는 저장 됐으나 발행되지 않은 모든 이벤트를 메시징 메커니즘을 통해 발행하기 위해 이벤트 저장소를 사용한다. 이는 모델과 이벤트가 단일 로컬 트랜잭션 안에서 일관성을 유지한다고 보장해주는 장점이 있다. 이 접근법은 저장소가 완전히 프라이빗한 메시징 인프라를 사용할 수 있도록 해준다. 하지만 이벤트 저장 후에 미들에어 메시징 메커니즘을 사용할 수 있는 상황에선, 메시징 메커니즘을 통해 발송하기 위해 반드시 이벤트 전달자를 직접 개발해야 하고, 클라이언트는 반드시 이벤트 전달자를 직접 개발해야 하고, 클라이언트는 반드시 들어오는 메시지 중복을 제거 하도록 설계돼야 한다는 단점이 있다.
다른 시스템을 호출하는 대신 비동기적 메시징을 사용해 시스템 사이에서 높은 수준의 독립성을 달성하자.
8-7. 지연 시간 허용
메시지를 수신할 때 오래 소요될 수도 있는 지연 시간이 문제를 유발하진 않을까? 분명히 이는 신중히 고려해야 하는 문제이며, 동기화되지 않은 데이터가 잘못된 영향을 미치거나 동작을 망까뜨리기도 한다. 상태의 일관성을 달성하기 위해 얼마나 긴 시간을 허용할지, 감당할 수 없는 한계는 어디인지를 질문해봐야 한다.
다음과 같은 질문이 의미 있는 답으로 이어질 수 있따. 컴퓨터를 다루기에 앞서 비즈니스 관계자는 어떻게 일하는가? 아마도 종이에 기반한 아주 간단한 시스템조차도 즉각적으로 일관성을 획득할 순 ㅇ없다. 따라서 자동화된 컴퓨터 시스템도 허용 범위가 있어야 하고, 결과적 일관성의 방식이 심지어 광범위하게 사용되고 있다는 사실도 이해할 수 있다. 결과적 일관성은 비즈니스적 맥락에서 더 잘 맞을지도 모른다.
최대 허용 지연 시간을 잘 이해해야 하며, 시스템 허용치를 만족시키면서도 더 잘 수행되도록 아키텍처 품질을 높여야 한다. 자치 서비스와 서비스에 지원하는 메시지 인프라는 반드시 고가용성과 확장성을 갖도록 설계해야 하고, 이를 통해 엔터프라이즈의 엄ㅁ격한 비기능적 요구사항을 충실히 만족시켜야 한다.
'DDD' 카테고리의 다른 글
| 10장. 애그리게잇 / IDDD (도메인 주도 설계 구현) (0) | 2022.03.16 |
|---|---|
| 9장. 모듈(패키지) / IDDD (도메인 주도 설계 구현) (0) | 2022.03.16 |
| 7장. 서비스 / IDDD (도메인 주도 설계 구현) (0) | 2022.03.15 |
| 6장. 값 객체 / IDDD (도메인 주도 설계 구현) (0) | 2022.03.15 |
| 5장. 엔티티 / IDDD (도메인 주도 설계 구현) (0) | 2022.03.14 |
