Bagaimana cara saya menangani efek samping dalam Pengadaan Acara?

14

Mari kita asumsikan bahwa kita ingin menerapkan subsistem keamanan kecil untuk aplikasi keuangan yang memperingatkan pengguna melalui email jika pola aneh terdeteksi. Untuk contoh ini, polanya akan terdiri dari tiga transaksi seperti yang digambarkan. Subsistem keamanan dapat membaca acara dari sistem utama dari antrian.

Yang ingin saya dapatkan adalah peringatan yang merupakan konsekuensi langsung dari peristiwa yang terjadi dalam sistem, tanpa representasi perantara yang memodelkan keadaan pola saat ini.

  1. Pemantauan diaktifkan
  2. Transaksi diproses
  3. Transaksi diproses
  4. Transaksi diproses
  5. Peringatan dipicu (id: 123)
  6. Email untuk lansiran terkirim (untuk id: 123)
  7. Transaksi diproses

Dengan memikirkan hal ini, saya pikir event sourcing dapat diterapkan di sini dengan sangat baik, meskipun saya memiliki pertanyaan tanpa jawaban yang jelas. Peringatan yang dipicu dalam contoh ini memiliki efek samping yang jelas, email harus dikirim, keadaan yang seharusnya hanya terjadi sekali. Karena itu, seharusnya tidak terjadi ketika memutar ulang semua peristiwa agregat.

Sampai taraf tertentu, saya melihat email yang perlu dikirim mirip dengan materialisasi yang dihasilkan oleh sisi permintaan yang telah saya lihat berkali-kali dalam literatur sumber CQRS / Event, dengan perbedaan yang tidak begitu halus sekalipun.

Dalam literatur ini, sisi permintaan dibangun dari penangan acara yang dapat menghasilkan materialisasi negara pada titik tertentu membaca lagi semua peristiwa. Namun dalam hal ini, itu tidak dapat dicapai persis seperti itu karena alasan yang dijelaskan sebelumnya. Gagasan bahwa setiap negara bersifat sementara tidak berlaku dengan baik di sini . Kita perlu mencatat fakta bahwa peringatan dikirim ke suatu tempat.

Solusi mudah bagi saya adalah memiliki tabel atau struktur yang berbeda tempat Anda menyimpan catatan peringatan yang sebelumnya dipicu. Karena kami memiliki ID, kami dapat memeriksa apakah peringatan dengan ID yang sama dikeluarkan sebelumnya. Memiliki informasi ini akan membuat idempotent SendAlertCommand. Beberapa perintah dapat dikeluarkan, tetapi efek samping hanya akan terjadi sekali.

Bahkan dengan memikirkan solusi itu, saya tidak tahu apakah ini merupakan petunjuk bahwa ada sesuatu yang salah dengan arsitektur ini untuk masalah ini.

  • Apakah pendekatan saya benar?
  • Apakah ada tempat di mana saya dapat menemukan informasi lebih lanjut tentang ini?

Sungguh aneh bahwa saya belum dapat menemukan informasi lebih lanjut tentang ini. Mungkin saya telah menggunakan kata-kata yang salah.

Terima kasih banyak!

Yakub
sumber

Jawaban:

12

Bagaimana cara saya menangani efek samping dalam Pengadaan Acara?

Versi singkat: model domain tidak melakukan efek samping. Itu melacak mereka. Efek samping dilakukan menggunakan port yang menghubungkan ke batas; ketika email dikirim, Anda mengirim pemberitahuan kembali ke model domain.

Ini berarti bahwa email dikirim di luar transaksi yang memperbarui aliran acara.

Di mana tepatnya, di luar, adalah soal selera.

Jadi secara konseptual, Anda memiliki aliran acara seperti

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

Dan dari aliran ini Anda dapat membuat lipatan

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

Lipatan memberi tahu Anda surel mana yang belum diakui, jadi Anda mengirimnya lagi:

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

Secara efektif, ini adalah komitmen dua fase: Anda memodifikasi SMTP di dunia nyata, dan kemudian Anda memperbarui model.

Pola di atas memberi Anda model pengiriman setidaknya satu kali. Jika Anda ingin paling banyak sekali, Anda dapat memutarnya

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

Ada penghalang transaksi antara membuat EmailPrepared tahan lama dan benar-benar mengirim email. Ada juga hambatan transaksi antara mengirim email dan membuat EmailDeliver menjadi tahan lama.

Perpesanan Tepercaya dari Udi Dahan dengan Transaksi Terdistribusi mungkin merupakan titik awal yang baik.

VoiceOfUnreason
sumber
2

Anda perlu memisahkan 'State Change Events' dari 'Actions'

Sebuah Negara Perubahan Kegiatan ini merupakan acara yang mengubah keadaan objek. Ini adalah yang Anda simpan dan putar ulang.

Sebuah Aksi adalah sesuatu benda tidak untuk hal-hal lain. Ini tidak disimpan sebagai bagian dari Pengadaan Acara.

Salah satu cara untuk melakukan ini adalah dengan event hander, yang Anda pasang atau tidak tergantung pada apakah Anda ingin menjalankan Actions.

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

Sekarang di layanan pemantauan saya, saya bisa

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
}

Jika Anda perlu mencatat surel yang dikirim, maka Anda dapat melakukannya sebagai bagian dari SendAlarmEmail. Tapi itu bukan peristiwa dalam arti Event Sourcing

Ewan
sumber