Chapter 10. 애그리거트 트랜잭션 관리

beanii·2023년 5월 8일
0

DDD Study

목록 보기
10/11
post-thumbnail

10.1 시스템 간 강결합 문제


  • 두 바운디드 컨텍스트 간의 강결합 때문에 서로 영향을 받음
    -> 이벤트를 사용해서 해결
  • 특히 비동기 이벤트를 사용하면 두 시스템 간의 결합 크게 낮출 수 있음

10.2 이벤트 개요


  • 이벤트(event) : 과거에 벌어진 어떤 것
  • 이벤트 발생 = 상태가 변경됨
  • 도메인의 상태 변경과 관련된 요구사항을 이벤트를 이용해서 구현

10.2.1 이벤트 관련 구성요소

  • 이벤트 도입을 위해 필요한 구성요소
    • 이벤트
    • 이벤트 생성 주체
    • 이벤트 디스패치(퍼블리셔)
    • 이벤트 핸들러(구독자)

  • 이벤트 생성 주체
    • 도메인 모델 관점에서 이벤트 생성 주체 -> 엔티티, 밸류 도메인 서비스와 같은 도메인 객체
    • 도메인 로직을 실행해서 상태가 바뀌면 관련 이벤트 발생

  • 이벤트 핸들러는 이벤트 생성 주체가 발생한 이벤트에 반응
    • 이벤트 새성 주체가 발생한 이벤트를 전달받아 이벤트에 담긴 데이터 이용해서 원하는 기능 실행

  • 이벤트 디스패치는 이벤트 생성 주체와 이벤트 핸들러 연결해 줌
    • 이벤트 디스채러의 구현 방식에 따라 이벤트 생성과 처리를 동기나 비동기로 실행하게 됨

10.2.2 이벤트의 구성

  • 이벤트는 발생한 이벤트에 대한 정보는 담음

    • 이벤트 종류: 클래스 이름으로 이벤트 종류를 표현
    • 이벤트 발생 시간
    • 추가 데이터: 주문번호, 신규 배송지 정보 등 이벤트와 관련된 정보
  • 이벤트 자체와 관련 없는 데이터를 포함할 필요는 없음

10.2.3 이벤트 용도

  1. 트리거(Trigger)
  • 도메인 상태가 바뀔 때 다른 후처리가 필요하면 후처리를 실행하기 위한 트리거로 이벤트 사용 가능
  1. 서로 다른 시스템 간의 데이터 동기화

10.2.4 이벤트 장점

  • 서로 다른 도메인 로직이 섞이는 것을 방지
  • 기능 확장 용이

10.3 이벤트, 핸들러, 디스패처 구현


이벤트와 관련된 코드

  • 이벤트 클래스: 이벤트를 표현함
  • 디스패처: 스프링이 제공하는 ApplicationEventPublisher를 이용
  • Events: 이벤트를 발생. 이벤트 발생을 위해 ApplicationEventPublisher 사용
  • 이벤트 핸들러: 이벤트 수신해서 처리. 스프링이 제공하는 기능 사용

10.3.1 이벤트 클래스

  • 이벤트 자체를 위한 상위 타입은 존재하지 않음

  • 원하는 클래스를 이벤트로 사용하되, 클래스명은 과거 시제로 설정해야 함

  • 이벤트를 처리하는 데 필요한 최소한의 데이터 포함

  • 모든 이벤트가 공통으로 갖는 프로퍼티가 존재한다면 관련 상위 클래스 생성 가능 -> 각 이벤트 클래스가 상속 받음

10.3.2 Events 클래스와 ApplicationEventPublisher

  • 이벤트 발생과 출판을 위해 스프링이 제공하는 ApplicationEventPublisher를 사용

  • ex) publishEvent()메서드 이용해서 이벤트 발생시킴, setPublicher()메서드를 통해서 Events 클래스가 사용할 ApplicationEventPublisher 객체를 전달받음

10.3.3 이벤트 발생과 이벤트 핸들러

  • Events.raise() 메서드를 사용해서 이벤트 발생시킴
  • 이벤트 핸들러는 스프링이 제공하는 @EventListener 애너테이션 사용해서 구현

10.3.4 흐름 정리

  1. 도메인 기능 실행
  2. 도메인 기능은 Events.raise()를 이용해서 이벤트 발생시킴
  3. Events.raise()는 스프링이 제공하는 ApplicationEventPublisher를 이용해서 이벤트 출판
  4. ApplicationEventPublisher@EventListener(이벤트타입.class) 애너테이션이 붙은 메서드를 찾아 실행함
  • 도메인 상태 변경과 이벤트 핸들러는 같은 트랜잭션 범위에서 실행

10.4 동기 이벤트 처리 문제


  • 강결합 문제는 해결, 하지만 외부 서비스에 영향 받는 문제 남아있음

  • 해결방법

    • 이벤트를 비동기로 처리
    • 이벤트와 트랜잭션을 연계하는 것

10.5 비동기 이벤트 처리


  • A 하면 이어서 B 하라 -> A 하면 최대 언제까지 B 하라
    -> 비동기로 처리하는 방식으로 구현 가능

  • 이벤트를 비동기로 구현하는 방법

    • 로컬 핸들러를 비동기로 실행하기
    • 메시징 큐를 사용하기
    • 이벤트 저장소와 이벤트 포워더 사용하기
    • 이벤트 저장소와 이벤트 제공 API 사용하기

10.5.1 로컬 핸들러 비동기 실행

  • 이벤트 핸들러를 별도 스레드로 실행
  • 스프링이 제공하는 @Async 애너테이션 사용하면 손쉽게 비동기 이벤트 핸들러 실행 가능
    • @EnableAsync 애너테이션을 사용해서 비동기 기능을 활성화함
    • 이벤트 핸들러 메서드에 @Async 애너테이션을 붙임

10.5.2 메시징 시스템을 이용한 비동기 구현

  • 카프카(Kafka)나 래빗MQ(RabbitMQ)와 같은 메시징시스템 사용

    • 이벤트 발생하면 이벤트 디스패처는 이벤트를 메시지 큐에 보냄
    • 메시지 큐는 이벤트를 메시지 리스너에 전달
    • 메시지 리스너는 알맞은 핸들러 이용해서 이벤트 처리
  • 글로벌 트랜잭션

    • 이벤트를 발생시키는 도메인 기능과 ㅔ시지 큐에 이벤트를 저장하는 절차를 한 트랜잭션으로 묶는 것
    • 장점: 안전하게 이벤트를 메시지 큐에 전달 가능
    • 단점 전체 성능 떨어짐, 글로벌 트랜잭션을 지원하지 않는 메시징 시스템도 있음
  • 래빗MQ

    • 장점: 글로벌 트랜잭션 지원과 함께 클러스터와 고가용성 지원하기 때문에 안정적으로 메시지 전달 가능, 다양한 개발 언어와 통신 프로토콜 ㅣ원
  • 카프카

    • 글로벌 트랜잭션을 지원하진 않지만 다른 메시징 시스템에 비해 높은 성능 보유

10.5.3 이벤트 저장소를 이용한 비동기 처리

  1. 이벤트를 일단 DB에 저장한 뒤에 별도 프로그램을 이용해서 이벤트 핸들러에 전달
  • 이벤트가 발생하면 핸들러는 스토리지에 이벤트 저장
  • 포워더는 주기적으로 이벤트 저장소에서 이벤트 가져와 이벤트 행들러 실행
  • 도메인의 상태와 이벤트 저장소로 동일한 DB 사용
  • 핸들러가 이벤트 처리에 실패할 경우 포워더는 다시 이벤트 저장소에서 이벤트 읽어와 핸들러 실행
  1. 이벤트를 외부에 제공하는 API 사용
  • 외부 핸들러가 API 서버를 통해 이벤트 목록 가져감
  • 이벤트 목록을 요구하는 외부 핸들러가 자신이 어디까지 이벤트를 처리했는지 기억해야 함

+) 이벤트 저장소 구현 방법
+) 이벤트 저장을 위한 이벤트 핸들러 구현 방법
+) REST API 구현 방법
+) 포워더 구현 방법


10.6 이벤트 적용 시 추가 고려 사항


  1. 이벤트 소스를 EventEntry애 추가할지 여부
  2. 포워더에서 전송 실패를 얼마나 허용할 것이냐
  3. 이벤트 손실에 대한 것
  4. 이벤트 순서에 대한 것
  5. 이벤트 재처리에 대한 것

10.6.1 이벤트 처리와 DB 트랜잭션 고려

  • 스프링이 지원하는 @TransactionalEventListener 애너테이션을 사용하여 스프링 트랜잭션 상태에 따라 이벤트 핸들러를 실행

0개의 댓글

관련 채용 정보