과제 관련 질문
과제를 진행하면서 배웠던 점 / 아쉬웠던 점
배웠던 점
- DDD 방법론 적용
- 이벤트 스토밍, 바운디드 컨텍스트 설전 경험
- DDD의 Layered 아키텍처 적용
- DDD에서 표현영역과 응용영역은 도메인 영역을 사용하고 도메인 영역은 인프라 영역을 사용
- 사용을 한다는 것은 의존성을 갖는다는 것인데 이것을 확인할 수 있는 방법이 import
- 고수준 레이어가 저수준 레이어에 의존하지 않기 위하여 노력함
- DIP 개념 도임
- 어그리거트 간의 의존성을 없애기 위하여 노력함
- 이벤트 드리븐 방식 도입(ApplicationEventPublisher)
- 어그리커트간 약한 참조(식별자를 활용)
- 직접 참조가 갖는 문제점
- 편리함을 오용
→ 한 에그리거트에서 다른 에그리거트 객체에 직접 접근하면 다른 에그리거트의 상태를 쉽게 변경할 수 있게됨
→ 한 에그리거트에서 다른 애그리거트의 상태를 변경하는 것은 에그리거트 간의 **의존 결합도(응집도)**를 높여서 결과적으로 애그리거트의 유연성을 어렵게 만듬
- 성능과 관련해서 여러가지 고민이 필요
→ JPA를 사용해서 참조한 객체를 지연로딩과 즉시로딩의 두 가지 방식으로 로딩할 수 있음
→ 두 로딩 방식 중 무엇을 사용할지는 에그리거트의 어떤 기능을 사용할지에 달렸음
→ 단순히 연관된 객체들을 함께 화면에 보여줘야하면 즉시 로딩이 유리하지만 상태를 변경하는 기능은 불필요한 객체를 함께 로딩할 필요는 없음
→ 그리고 트랜잭션은 작으면 작을 수록 좋음(한 애그리거트가 관리하는 범위는 자기 자신으로 한정해야함)
→ 응집도 상승 및 다른 어그리거트와 결합도를 낮출 수있으며 확장이 용이함
→ 또한 DB에 관련해서 여러 개의 테이블을 수정하기 위해서 X-LOCK을 잡고 있으면 동시성이 매우 떨어짐
- 확장
→ 초기에는 단일 서버에 단일 DBMS로 서비스를 제공하는 것이 가능
→ 하지만 사용자가 몰리기 시작하면 자연스럽게 부하를 분산하기 위해서 하위 도메인별로 시스템을 분리하기 시작
→ 이 과정에서 도메인마다 서로 다른 DBMS를 사용할 때도 있음
→ 하지만 여러 도메인의 결합도가 높으면 분리하기가 쉽지 않음
- 약한 참조
- 에그리거트의 경계를 명확히 하고 에그리거트 간 물리적인 연결을 제거하기 때문에 모델의 복잡도를 낮춤
- 또한 의존을 제거하므로 응집도를 높혀줌
- 동시성을 위한 트랜잭션, 락의 개념에 대한 공부
- TC, 인수테스트 작성 → 개발성 용이
아쉬운 점
- 이벤트 핸들링에 대한 모니터링 부족
- 이벤트 저장소를 이용한 비동기 처리로 작업
- 포인트가 변경되어 이벤트를 발행했지만, 포인트 히스토리를 처리하는 부분에서 예외가 발생할 수 있음 → 데이터가 유실될 수 있음
- 포인트 변경에 의한 이벤트 발행과 이벤트 저장을 하나의 트랜잭션으로 묶어서 관리 → 물론… 이벤트 저장이 실패하면 포인트 변경도 실패하지만.. 이벤트 유실을 막는게 중요하다면…
- 그려면 이벤트 리스너 쪽에서 포인트 변경 히스토리를 저장하다가 실패해도 물리적인 저장소에 이벤트가 저장되기 때문에 다시 읽어오면됨
- 이렇게 했을 때 단점은 어디까지 이벤트를 처리했는지 관리가 중요함
- UsePointPolicy 도메인 서비스
- 도메인 서비스는 도메인 영역에 위치한 도메인 로직을 구현할 때 주로 사용하며 주로 상황에서 사용
- 계산 로직: 여러 에그리거트가 필요한 계산 로직이나, 한 애그리거트에 넣기에는 다소 복잡한 계산 로직
- 외부 시스템 연동이 필요한 도메인 로직
- 할인 금액 규칙 계산이 대표적인 예시
- 유저 등급별 할인, 쿠폰 할인, 상품 자체 할인 등 여러 도메인 정보가 섞여 있음
- 이런 경우 도메인 서비스를 만들어서 개발
- 하지만 한가지 실수한 것이 다른 어그리거트의 도메인 객체를 가지고 왔음
- 포인트를 사용할 수 있는 권한을 가졌는지를 판단하기 위해서 멤버 어그리거트와 멤버 관계 어그리거트가 필요한데 포인트 어그리커트에서 직접 참조를 하였음
- 어그리거트 패키지 간의 의존관계를 약한 참조로 하여 패키지가 분리되더라도 쉽게 끊어낼 수 있어야하는데 위와 같이 코드를 작성한 것은 직접적으로 참조한 것임
- 표현 영역 요청에 대한 Validation, 도메인 영역의 Validation
- 이벤트 스토밍 이후 Boris다이어그램, SnapE를 하지 못하였음
- Boris 다이어그램은 어그리거트들 간의 통신을 선으로 그린 것(동기요청, 비동기 요청)
- SnapE는 해당 어그리거트가 어떠한 API를 가지고, 어떠한 유저 스토리를 가지고, 어떠한 화면을 가지는지 정의하는 것
- 이벤트 스토밍에서 포인트 히스토리 어그리거트를 붙이지 않음
CQRS로 패키지를 구성한 이유
보통 조회 API는 단일 에그리거트에서 데이터를 가져오는 것보다 여러 에그리거트를 조합해서 가져오게 됨
조회 특성상 조회 속도가 빠를수록 좋은데 도메인 간 응집도를 높히기 위해서 간접 참조를 사용한 상태 → JPA에서 지원하는 즉시 로딩을 사용할 수 없음
이는 SELECT 쿼리로 조회 화면에 필요한 데이터를 읽어올 수 없어서 성능에 큰 문제가 발생할 수 있음
따라서 커맨드와 쿼리를 분리해서 간접 참조를 사용한건 그대로 유지해서 어그리거트간의 응집도를 높히고 조회용 모델을 따로 정의하는 방법을 선택함
사실 이런 고민이 발생하는 이유는 시스템 상태를 변경할 때와 조회할 때 범위가 다름에도 불구하고 단일 도메인 모델을 사용하려고 하기 때문!
(이벤트 소싱 - CQRS 연관성 공부한 내용 참고)