ฉันจะจัดการกับผลข้างเคียงในการจัดหากิจกรรมได้อย่างไร


14

สมมติว่าเราต้องการใช้ระบบย่อยความปลอดภัยขนาดเล็กสำหรับแอปพลิเคชันทางการเงินที่เตือนผู้ใช้ทางอีเมลหากตรวจพบรูปแบบแปลก ๆ สำหรับตัวอย่างนี้รูปแบบจะประกอบด้วยสามธุรกรรมตามที่อธิบายไว้ ระบบย่อยความปลอดภัยสามารถอ่านเหตุการณ์จากระบบหลักจากคิว

สิ่งที่ฉันต้องการได้รับคือการแจ้งเตือนที่เป็นผลโดยตรงจากเหตุการณ์ที่เกิดขึ้นในระบบโดยไม่มีการแสดงสื่อกลางที่เป็นแบบจำลองสถานะปัจจุบันของรูปแบบ

  1. เปิดใช้งานการตรวจสอบแล้ว
  2. ประมวลผลธุรกรรมแล้ว
  3. ประมวลผลธุรกรรมแล้ว
  4. ประมวลผลธุรกรรมแล้ว
  5. ทริกเกอร์แจ้งเตือน (id: 123)
  6. ส่งอีเมลเพื่อรับการแจ้งเตือน (สำหรับ id: 123)
  7. ประมวลผลธุรกรรมแล้ว

เมื่อนึกถึงสิ่งนี้ฉันคิดว่าการจัดหากิจกรรมสามารถทำได้ที่นี่เป็นอย่างดีแม้ว่าฉันจะมีคำถามที่ไม่มีคำตอบที่ชัดเจน การแจ้งเตือนที่ทริกเกอร์ในตัวอย่างมีผลข้างเคียงที่ชัดเจนจำเป็นต้องส่งอีเมลสถานการณ์ที่ควรเกิดขึ้นเพียงครั้งเดียวเท่านั้น ดังนั้นจึงไม่ควรเกิดขึ้นเมื่อเล่นซ้ำเหตุการณ์ทั้งหมดของการรวมซ้ำ

ในระดับหนึ่งฉันเห็นอีเมลที่ต้องส่งคล้ายกับการสร้างเนื้อหาที่สร้างขึ้นโดยฝ่ายแบบสอบถามที่ฉันเห็นมาหลายครั้งในวรรณกรรมการจัดหา CQRS / กิจกรรมด้วยความแตกต่างที่ไม่ลึกซึ้งนัก

ในวรรณคดีนี้ด้านแบบสอบถามถูกสร้างขึ้นจากตัวจัดการเหตุการณ์ที่สามารถสร้างเป็นตัวเป็นตนของรัฐที่จุดที่กำหนดให้อ่านเหตุการณ์ทั้งหมดอีกครั้ง ในกรณีนี้แม้ว่าจะไม่สามารถบรรลุได้อย่างสมบูรณ์เช่นนั้นด้วยเหตุผลที่อธิบายไว้ก่อนหน้านี้ ความคิดที่ว่าทุกรัฐเป็นชั่วคราวใช้ไม่ได้เป็นอย่างดีที่นี่ เราต้องบันทึกความจริงที่ว่าการแจ้งเตือนถูกส่งไปที่ไหนสักแห่ง

ทางออกที่ง่ายสำหรับฉันคือการมีตารางหรือโครงสร้างที่แตกต่างกันซึ่งคุณเก็บบันทึกการแจ้งเตือนที่ถูกเรียกใช้ก่อนหน้านี้ เนื่องจากเรามี ID เราจะสามารถตรวจสอบว่ามีการออกการแจ้งเตือนด้วย ID เดียวกันก่อนหน้านี้หรือไม่ การมีข้อมูลนี้จะทำให้ SendAlertCommand idempotent สามารถออกคำสั่งได้หลายคำสั่ง แต่ผลข้างเคียงจะเกิดขึ้นเพียงครั้งเดียว

แม้จะมีวิธีแก้ปัญหานั้นในใจฉันก็ไม่รู้ว่านี่เป็นคำใบ้ว่ามีบางอย่างผิดปกติกับสถาปัตยกรรมนี้สำหรับปัญหานี้หรือไม่

  • วิธีการของฉันถูกต้องหรือไม่
  • มีสถานที่ใดบ้างที่ฉันสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้?

มันแปลกที่ฉันไม่สามารถหาข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ บางทีฉันอาจใช้ข้อความที่ไม่ถูกต้อง

ขอบคุณมาก!

คำตอบ:


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) );
}     

อย่างมีประสิทธิภาพนี่เป็นการกระทำสองขั้นตอน: คุณกำลังปรับเปลี่ยน 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.