답변:
읽기 모델의 상태를 수정하는 이벤트와 외부 시스템의 상태를 수정하는 이벤트를 명확하게 구분해야합니다. 두 상태를 함께 수정하는 "혼합 이벤트"가 없는지 확인하십시오. 이렇게하면 외부 시스템에 대한 이벤트가 다시 발생하지 않는 특정 "재생 모드"에서 이벤트를 재생할 수 있습니다. 이 모드에서는 외부 시스템에 의해 원래 시작된 모든 이벤트를 "시뮬레이션"합니다 (이제 대신 재생 큐에서 가져옵니다).
다시 배포 단계는 실제로 읽기 모델의 상태를 이전 시점으로 재설정하는 것을 의미합니다. 이것은 아마도 외부 시스템의 상태에 대해 할 수있는 (또는해야 할) 아무것도 아닙니다.
Martin Fowler의 이벤트 소싱 기사에서 :
이벤트 소싱의 기본 개념은 응용 프로그램 상태에 대한 모든 변경 사항이 이벤트 개체에 캡처되고 이러한 이벤트 개체가 응용 프로그램 상태와 동일한 수명 동안 적용된 순서대로 저장된다는 것입니다.
따라서 시스템 상태를 특정 시점으로 복원해야 할 때 이벤트 핸들러가 아닌 저장된 상태를 그 시점까지 재생합니다 .
즉, 상태 데이터로만 작업하는 경우 외부 시스템에 영향을 미치지 않아야합니다. 이벤트 저장소에 트리거 또는 감시자가없는 경우 복원 기간 동안이를 비활성화해야합니다. 외부 시스템에 대한 제어 권한이 없다고 말했기 때문에 노출 된 API를 사용하여 시스템에 어떤 부작용이 있는지 알지 못하므로 노출 된 API를 사용하여 상태를 복원하려고 시도해서는 안됩니다. 복원이 시스템을 중간 상태 (예 : 외부 시스템의 작동 실패로 인해)로 만들면 이벤트 재생의 책임에 속하지 않아야합니다.
그러나 이벤트 중 하나만 수행하면 이벤트를 재생하는 경우 해당 항목이 두 번 배송 될 경우 제어 시스템에없는 외부 시스템으로 인해 고객에게 "항목을 배송"해야하는 경우가 있습니다.
특정 예를 선택하기 위해 부작용에 대한 "적어도 한 번"접근 방식이 어떻게 작동하는지 고려해 봅시다.
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()
}
여기서 요점은