이벤트 소싱에서 부작용을 어떻게 처리합니까?


14

이상한 패턴이 감지되면 전자 메일을 통해 사용자에게 경고하는 금융 응용 프로그램을위한 작은 보안 하위 시스템을 구현한다고 가정합니다. 이 예에서 패턴은 세 가지 트랜잭션으로 구성됩니다. 보안 서브 시스템은 기본 시스템의 이벤트를 큐에서 읽을 수 있습니다.

내가 얻고 싶은 것은 패턴의 현재 상태를 모델링하는 중간 표현없이 시스템에서 발생하는 이벤트의 직접적인 결과 인 경고입니다.

  1. 모니터링 활성화
  2. 거래 처리
  3. 거래 처리
  4. 거래 처리
  5. 경고 발생 (ID : 123)
  6. 알림 이메일 전송 (ID : 123)
  7. 거래 처리

이 점을 염두에두면 이벤트 소싱이 여기에 잘 적용될 수 있다고 생각했지만 명확한 대답이없는 질문이 있습니다. 예제에서 트리거 된 경고는 분명한 부작용이 있으며 전자 메일을 보내야하며 상황은 한 번만 발생해야합니다. 따라서 집계의 모든 이벤트를 재생할 때 발생하지 않아야합니다.

어느 정도까지는 CQRS / 이벤트 소싱 문헌에서 여러 번 보았던 쿼리 측에서 생성 된 구체화와 유사하게 전송되어야하는 전자 메일을 볼 수 있지만 미묘한 차이는 없습니다.

이 문헌에서, 질의 측은 모든 이벤트를 다시 읽는 주어진 시점에서 상태의 구체화를 생성 할 수있는 이벤트 핸들러로부터 구축된다. 그러나이 경우에는 앞에서 설명한 이유로 정확히 달성 할 수 없습니다. 모든 상태가 일시적이라는 아이디어는 여기서 잘 적용되지 않습니다 . 어딘가에 경고가 전송되었다는 사실을 기록해야합니다.

나에게 쉬운 해결책은 이전에 트리거 된 경고의 기록을 유지하는 다른 테이블이나 구조를 갖는 것입니다. ID가 있으므로 이전에 동일한 ID를 가진 경고가 발행되었는지 확인할 수 있습니다. 이 정보가 있으면 SendAlertCommand를 dem 등원으로 만듭니다. 여러 명령을 실행할 수 있지만 부작용은 한 번만 발생합니다.

이 솔루션을 염두에 두었더라도 이것이이 문제에 대해이 아키텍처에 문제가 있음을 암시하는 것은 알 수 없습니다.

  • 내 접근 방식이 맞습니까?
  • 이에 대한 자세한 정보를 찾을 수있는 곳이 있습니까?

이것에 대한 더 많은 정보를 찾을 수 없었습니다. 어쩌면 나는 잘못된 문구를 사용하고 있었을 것입니다.

정말 고맙습니다!

답변:


12

이벤트 소싱에서 부작용을 어떻게 처리합니까?

짧은 버전 : 도메인 모델은 부작용을 수행하지 않습니다. 그것들을 추적 합니다. 부작용은 경계에 연결된 포트를 사용하여 수행됩니다. 이메일이 전송되면 승인을 도메인 모델로 다시 보냅니다.

이는 이메일이 이벤트 스트림을 업데이트하는 트랜잭션 외부로 전송됨을 의미 합니다.

정확하게 외부의 맛의 문제입니다.

개념적으로는 다음과 같은 일련의 이벤트가 있습니다.

EmailPrepared(id:123)
EmailPrepared(id:456)
EmailPrepared(id:789)
EmailDelivered(id:456)
EmailDelivered(id:789)

이 스트림에서 접기를 만들 수 있습니다

{
    deliveredMail : [ 456, 789 ],
    undeliveredMail : [123]
}

접힌 부분은 확인되지 않은 이메일을 알려주므로 다시 보내십시오.

undeliveredMail.each ( mail -> {
    send(mail);
    dispatch( new EmailDelivered.from(mail) );
}     

실제로 이것은 2 단계 커밋입니다. 실제 환경에서 SMTP를 수정 한 다음 모델을 업데이트하고 있습니다.

위의 패턴은 최소한의 전달 모델을 제공합니다. 한 번만 원한다면 뒤집을 수 있습니다

undeliveredMail.each ( mail -> {
    commit( new EmailDelivered.from(mail) );
    send(mail);
}     

EmailPrepared를 내구성있게 만드는 것과 실제로 이메일을 보내는 것 사이에는 거래 장벽이 있습니다. 이메일 전송과 EmailDelivered의 내구성 유지 사이에는 거래 장벽도 있습니다.

분산 트랜잭션을 사용한 Udi Dahan의 안정적인 메시징 은 좋은 출발점이 될 수 있습니다.


2

'상태 변경 이벤트'와 '액션'을 분리해야합니다.

상태 변경 이벤트 객체의 상태를 변경하는 이벤트입니다. 이것들은 당신이 저장하고 재생하는 것들입니다.

조치 오브젝트가 다른 일을하기 위해 수행하는 것입니다. 이들은 이벤트 소싱의 일부로 저장되지 않습니다.

이 작업을 수행하는 한 가지 방법은 이벤트 처리기를 사용하는 것입니다. 이벤트 처리기는 작업 실행 여부에 따라 연결됩니다.

public class Monitor
{
    public EventHander SoundAlarm;
    public void MonitorEvent(Event e)
    {
        this.eventcount ++;
        if(this.eventcount > 10)
        {
             this.state = "ALARM!";
             if(SoundAlarm != null) { SoundAlarm();}
        }
    }
}

이제 내 모니터링 서비스에서

public void MonitorServer()
{
    var m = new Monitor(events); //11 events
    //alarm has not been sounded because the event handler wasn't wired up
    //but the internal state is correctly set to "ALARM!"
    m.SoundAlarm += this.SendAlarmEmail;
    m.MonitorEvent(e); //email is sent
}

전송 된 이메일을 기록해야하는 경우 SendAlarmEmail의 일부로 로그인 할 수 있습니다. 그러나 이벤트 소싱의 의미에서 이벤트가 아닙니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.