이벤트를 재생할 때 CRQS의 부작용을 처리하는 방법은 무엇입니까?


10

CQRS에서는 버그를 쉽게 수정할 수 있다고 말하면 이벤트를 재배치 한 다음 재생하면됩니다.

그러나 이벤트 중 하나만 수행하면 이벤트를 재생하는 경우 해당 항목이 두 번 배송 될 경우 제어 시스템에없는 외부 시스템으로 인해 고객에게 "항목을 배송"해야하는 경우가 있습니다.

어떻게 해결합니까?

답변:


6

읽기 모델의 상태를 수정하는 이벤트와 외부 시스템의 상태를 수정하는 이벤트를 명확하게 구분해야합니다. 두 상태를 함께 수정하는 "혼합 이벤트"가 없는지 확인하십시오. 이렇게하면 외부 시스템에 대한 이벤트가 다시 발생하지 않는 특정 "재생 모드"에서 이벤트를 재생할 수 있습니다. 이 모드에서는 외부 시스템에 의해 원래 시작된 모든 이벤트를 "시뮬레이션"합니다 (이제 대신 재생 큐에서 가져옵니다).

다시 배포 단계는 실제로 읽기 모델의 상태를 이전 시점으로 재설정하는 것을 의미합니다. 이것은 아마도 외부 시스템의 상태에 대해 할 수있는 (또는해야 할) 아무것도 아닙니다.


"재배포 단계는 실제로 읽기 모델의 상태를 이전 시점으로 재설정하는 것을 의미합니다. 이는 아마도 외부 시스템의 상태에 대해 수행 할 수있는 (또는 수행해야 할) 조치 일 것입니다." -> 그러나 리플레이에서 배송과 같은 외부 시스템 호출에 실패한 경우 어떻게합니까? 이 경우 재생을 다시 배포하면 읽기 모델의 상태가 재설정 될뿐만 아니라 외부 이벤트가 발생할 수 있습니다. 이것은 공정하게 들리거나 뭔가 빠졌습니까?
Jas

2
@Jas : 실패한 외부 시스템 호출을 다시 시도하기 위해 "재생"을 남용하지 않습니다. "재생"을 사용하여 이전과 동일한 상태로 시스템의 읽기 모델을 가져옵니다. 즉, 운송 요청이 실패한 경우 시스템에이 실패에 대한 정보가 제공되고 해당 정보가 해당 상태에 저장됩니다. 재생은이 정보가 "재배치 및 재생"이후에도 계속 유지되도록합니다. 따라서 재생 후 시스템이 "실패시 배송 재시도"전략을 적용 할 수 있습니다 (CQRS와 관련이없는 강력한 주문 시스템은 이러한 전략을 가져야 함).
Doc Brown

흥미롭게도, 이것은 내가 생각했던 것인데, 이것에 "패턴"이 있는지 궁금해서 바퀴를 재발 명하지 않았습니다!
Jas

3

Martin Fowler의 이벤트 소싱 기사에서 :

이벤트 소싱의 기본 개념은 응용 프로그램 상태에 대한 모든 변경 사항이 이벤트 개체에 캡처되고 이러한 이벤트 개체가 응용 프로그램 상태와 동일한 수명 동안 적용된 순서대로 저장된다는 것입니다.

따라서 시스템 상태를 특정 시점으로 복원해야 할 때 이벤트 핸들러가 아닌 저장된 상태를 그 시점까지 재생합니다 .

즉, 상태 데이터로만 작업하는 경우 외부 시스템에 영향을 미치지 않아야합니다. 이벤트 저장소에 트리거 또는 감시자가없는 경우 복원 기간 동안이를 비활성화해야합니다. 외부 시스템에 대한 제어 권한이 없다고 말했기 때문에 노출 된 API를 사용하여 시스템에 어떤 부작용이 있는지 알지 못하므로 노출 된 API를 사용하여 상태를 복원하려고 시도해서는 안됩니다. 복원이 시스템을 중간 상태 (예 : 외부 시스템의 작동 실패로 인해)로 만들면 이벤트 재생의 책임에 속하지 않아야합니다.


2

그러나 이벤트 중 하나만 수행하면 이벤트를 재생하는 경우 해당 항목이 두 번 배송 될 경우 제어 시스템에없는 외부 시스템으로 인해 고객에게 "항목을 배송"해야하는 경우가 있습니다.

특정 예를 선택하기 위해 부작용에 대한 "적어도 한 번"접근 방식이 어떻게 작동하는지 고려해 봅시다.

State currentState = State.InitialState
for(Event e : events) {
    currentState = currentState.apply(e)
}
for(SideEffect s : currentState.querySideEffects()) {
    performSideEffect(s)

따라서 도메인 모델은 수행해야 할 작업을 추적합니다. 그러나 실제 작업을 응용 프로그램에 맡깁니다.

명령을 실행하는 맥락에서 기본 아이디어는 동일하게 보입니다. 실제 부작용 은 모델을 업데이트하는 트랜잭션 외부 에서 발생 합니다.

따라서 모델의 단위 테스트는 다음과 같이 보일 수 있습니다.

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced)

    // Then
    List.of(SendEmail) === currentState.applyAll(events).querySideEffects()
}

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced, EmailSent)

    // Then
    List.EMPTY === currentState.applyAll(events).querySideEffects()
}

여기서 요점은

  • 모델 업데이트는 부작용이 없습니다. 실제 부작용은 모델을 업데이트하는 트랜잭션 외부에서 발생합니다.
  • 부작용의 결과를 설명하는 이벤트가 다시 발생해야합니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.