Bagaimana cara menangani efek samping dalam CRQS saat memutar ulang acara?

10

Dikatakan bahwa di CQRS mudah untuk memperbaiki bug, Anda cukup memindahkan dan kemudian memutar ulang acara tersebut.

Tetapi, bagaimana jika salah satu peristiwa menyebabkan sistem eksternal yang tidak dalam kendali Anda untuk "mengirimkan barang" kepada pelanggan jika Anda hanya memutar ulang acara, barang tersebut akan dikirim dua kali.

Bagaimana Anda mengatasinya?

Jas
sumber

Jawaban:

6

Anda perlu membuat pemisahan yang jelas antara peristiwa memodifikasi keadaan model baca Anda, dan peristiwa (berpotensi) memodifikasi keadaan sistem eksternal. Pastikan Anda tidak memiliki "acara campuran" yang memodifikasi kedua negara secara bersamaan. Dengan begitu, Anda dapat memutar ulang acara Anda dalam "mode replay" tertentu di mana peristiwa untuk sistem eksternal tidak dipecat lagi. Dalam mode ini, Anda juga "mensimulasikan" peristiwa apa pun yang semula diprakarsai oleh sistem eksternal (sekarang Anda mengambilnya dari antrian replay sebagai gantinya).

Jangan lupa, langkah penempatan ulang sebenarnya berarti mengatur ulang keadaan model baca ke titik waktu sebelumnya. Ini mungkin bukan apa-apa yang dapat Anda lakukan (atau perlu lakukan) untuk keadaan sistem eksternal.

Doc Brown
sumber
"Jangan lupa, langkah penempatan ulang sebenarnya berarti mengatur ulang keadaan model baca ke titik waktu sebelumnya. Ini mungkin bukan apa-apa yang dapat Anda lakukan (atau perlu lakukan) untuk keadaan sistem eksternal." -> tetapi bagaimana jika saya ingin replay saya mencoba lagi panggilan sistem eksternal yang gagal seperti pengiriman? dalam hal ini pemindahan saya replay tidak hanya akan mereset keadaan model baca tetapi juga menyebabkan peristiwa eksternal, apakah ini terdengar adil atau saya kehilangan sesuatu?
Jas
2
@Jas: Anda tidak ingin menyalahgunakan "replay" untuk mencoba kembali panggilan sistem eksternal yang gagal. Anda menggunakan "replay" untuk mendapatkan model baca sistem Anda sendiri dalam kondisi yang sama seperti sebelumnya. Itu berarti dalam kasus permintaan pengiriman yang gagal, sistem Anda telah diberitahu sebelumnya tentang kegagalan ini dan menyimpan informasi itu di suatu tempat di negaranya. Replay memastikan informasi ini masih ada setelah "redeploy & replay". Jadi setelah replay, sistem Anda mungkin menerapkan strategi "coba pengiriman jika terjadi kegagalan" (yang tidak ada hubungannya dengan CQRS, setiap sistem pemesanan yang kuat harus memiliki strategi seperti itu).
Doc Brown
Menarik, ini adalah apa yang ada dalam pikiran saya untuk lakukan, hanya ingin tahu apakah ada "pola" pada ini jadi saya tidak menemukan kembali roda!
Jas
3

Dari artikel sumber acara Martin Fowler :

Gagasan mendasar dari Pengadaan Acara adalah memastikan setiap perubahan kondisi aplikasi ditangkap dalam objek acara, dan bahwa objek acara ini sendiri disimpan dalam urutan yang diterapkan untuk masa yang sama dengan status aplikasi itu sendiri.

Jadi, ketika Anda perlu mengembalikan keadaan sistem Anda ke saat tertentu saat Anda memutar ulang kondisi yang disimpan , bukan penangan acara, hingga saat itu.

Yang sedang berkata, jika Anda hanya bekerja dengan data negara, seharusnya tidak ada efek pada sistem eksternal. Kecuali jika Anda memiliki pemicu atau pengamat di toko acara Anda dalam hal ini Anda harus menonaktifkannya selama pemulihan. Karena Anda mengatakan bahwa Anda tidak memiliki kendali atas sistem eksternal, seharusnya tidak ada upaya untuk memulihkan keadaannya dengan menggunakan API yang terbuka karena Anda tidak tahu apa efek samping yang mungkin terjadi pada sistem mereka. Jika pemulihan menempatkan sistem dalam kondisi peralihan (mis. Karena operasi yang gagal dalam sistem eksternal) ini seharusnya tidak berada dalam tanggung jawab replay peristiwa.

devnull
sumber
2

Tetapi, bagaimana jika salah satu peristiwa menyebabkan sistem eksternal yang tidak dalam kendali Anda untuk "mengirimkan barang" kepada pelanggan jika Anda hanya memutar ulang acara, barang tersebut akan dikirim dua kali.

Untuk memilih contoh spesifik, mari pertimbangkan bagaimana pendekatan "setidaknya satu kali" untuk efek samping dapat bekerja.

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

Jadi model domain melacak apa yang perlu dilakukan; tetapi meninggalkan tindakan aktual ke aplikasi

Dalam konteks menjalankan perintah, ide dasarnya terlihat sama. Efek samping yang sebenarnya terjadi di luar transaksi yang memperbarui model.

Jadi tes unit untuk model Anda bisa terlihat seperti

{
    // 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()
}

Poin utama di sini adalah

  • Memperbarui model ini bebas efek samping; efek samping yang sebenarnya terjadi di luar transaksi yang memperbarui model.
  • Suatu peristiwa yang menggambarkan hasil dari efek samping perlu kembali.
VoiceOfUnreason
sumber