Kafka event streaming เปลี่ยนการออกแบบระบบโดยมองเหตุการณ์เป็นล็อกเรียงลำดับ เรียนรู้ว่าเมื่อใดคิวเพียงพอ และเมื่อใดที่แนวทางแบบล็อกให้ประโยชน์มากกว่า

สินค้าส่วนใหญ่เริ่มจากการผสานแบบจุดต่อจุดที่เรียบง่าย: ระบบ A เรียกระบบ B หรือสคริปต์เล็ก ๆ คัดลอกข้อมูลจากที่หนึ่งไปอีกที่ มันใช้ได้จนกว่าสินค้าจะเติบโต ทีมแยกกัน และจำนวนการเชื่อมต่อเพิ่มขึ้น ในไม่ช้าการเปลี่ยนแปลงแต่ละครั้งต้องการการประสานงานข้ามหลายบริการ เพราะฟิลด์เล็ก ๆ หรือสถานะเดียวสามารถกระเพื่อมผ่านห่วงโซ่ของการพึ่งพาได้
ความเร็วมักเป็นสิ่งแรกที่พัง การเพิ่มฟีเจอร์ใหม่หมายถึงการอัปเดตการผสานหลายจุด การดีพลอยหลายบริการ และหวังว่าไม่มีอะไรพึ่งพาพฤติกรรมเก่า
จากนั้นการดีบักก็เจ็บปวด เมื่อบางอย่างดูผิดใน UI คำถามพื้นฐานก็ยากจะตอบ: เกิดอะไรขึ้น ลำดับเหตุการณ์เป็นอย่างไร และระบบไหนเป็นคนเขียนค่าที่คุณเห็น?
สิ่งที่ขาดมักเป็นแทรลตรวจสอบ ถ้าข้อมูลถูกผลักตรงจากฐานข้อมูลหนึ่งไปยังอีกฐาน (หรือถูกแปลงระหว่างทาง) คุณจะสูญเสียประวัติ คุณอาจเห็นสถานะสุดท้าย แต่ไม่เห็นลำดับเหตุการณ์ที่นำไปสู่สถานะนั้น การทบทวนเหตุการณ์และการสนับสนุนลูกค้าต่างได้รับผลกระทบเพราะคุณไม่สามารถเล่นซ้ำอดีตเพื่อตรวจสอบว่าอะไรเปลี่ยนและเพราะอะไร
นี่แหละที่โต้แย้งเรื่อง “ใครเป็นแหล่งความจริง” เริ่มขึ้น ทีมหนึ่งบอกว่า “บริการบิลลิ่งเป็นแหล่งความจริง” อีกทีมบอกว่า “บริการออร์เดอร์เป็น” ในความเป็นจริง แต่ละระบบมีมุมมองบางส่วน และการเชื่อมต่อจุดต่อจุดทำให้ความขัดแย้งนั้นกลายเป็นแรงเสียดทานประจำวัน
ตัวอย่างง่าย ๆ: ออร์เดอร์ถูกสร้าง ถูกชำระเงิน แล้วถูกคืนเงิน หากสามระบบอัปเดตกันโดยตรง แต่ละระบบอาจมีเรื่องราวต่างกันเมื่อเกิดการรีไทร เวลาออก หรือการแก้ไขด้วยมือ
นั่นนำไปสู่คำถามการออกแบบหลักเบื้องหลัง Kafka event streaming: คุณแค่ต้องย้ายงานจากที่หนึ่งไปยังอีกที่หนึ่ง (คิว) หรือคุณต้องการบันทึกร่วมที่ทนทานของสิ่งที่เกิดขึ้นซึ่งหลายระบบอ่าน สะบัดกลับ และเชื่อถือได้ (ล็อก)? คำตอบเปลี่ยนวิธีที่คุณสร้าง ดีบัก และพัฒนาระบบของคุณ
Jay Kreps มีส่วนช่วยในการกำหนด Kafka และที่สำคัญกว่านั้นคือวิธีที่หลายทีมคิดเกี่ยวกับการเคลื่อนย้ายข้อมูล การเปลี่ยนแนวคิดที่มีประโยชน์คือ: หยุดมองข้อความเป็นการส่งครั้งเดียว แล้วเริ่มมองกิจกรรมของระบบเป็นบันทึก
แนวคิดหลักเรียบง่าย ออกแบบการเปลี่ยนแปลงสำคัญเป็นสตรีมของข้อเท็จจริงที่ไม่เปลี่ยนแปลง:
เหตุการณ์แต่ละรายการเป็นข้อเท็จจริงที่ไม่ควรแก้ไขย้อนหลัง หากสิ่งใดเปลี่ยนภายหลัง คุณเพิ่มเหตุการณ์ใหม่ที่ระบุความจริงใหม่ เมื่อเวลาผ่านไป ข้อเท็จจริงเหล่านั้นรวมกันเป็นล็อก: ประวัติที่เพิ่มต่อท้ายเท่านั้น
นี่แหละที่ Kafka event streaming แตกต่างจากการตั้งค่าข้อความพื้นฐานจำนวนมาก คิวจำนวนมากถูกออกแบบรอบ ๆ การ "ส่ง ประมวลผล ลบ" ซึ่งใช้ได้เมื่องานเป็นการส่งต่อเท่านั้น มุมมองล็อกบอกว่า "เก็บประวัติไว้เพื่อให้ผู้บริโภคหลายรายใช้มันได้ ทั้งตอนนี้และในภายหลัง"
การเล่นซ้ำประวัติเป็นพลังที่ใช้งานได้จริง
ถ้ารายงานผิด คุณสามารถรันประวัติเดียวกันผ่านงานวิเคราะห์ที่แก้ไขแล้วและดูว่าตัวเลขเปลี่ยนที่ไหน หากบั๊กทำให้อีเมลผิดพลาด คุณสามารถเล่นซ้ำเหตุการณ์ในสภาพแวดล้อมทดสอบและสร้างไทม์ไลน์เดียวกันได้ หากฟีเจอร์ใหม่ต้องการข้อมูลในอดีตก็สามารถสร้างผู้บริโภคใหม่ที่เริ่มจากจุดเริ่มต้นและไล่ตามทันในจังหวะของตัวเอง
ตัวอย่างชัดเจน: สมมติว่าคุณเพิ่มการตรวจจับการฉ้อโกงหลังจากที่ได้ประมวลผลการชำระเงินเป็นเดือน ๆ แล้ว ด้วยล็อกของเหตุการณ์การชำระเงินและบัญชี คุณสามารถเล่นซ้ำอดีตเพื่อฝึกหรือตั้งค่ากฎจากลำดับจริง คำนวณคะแนนความเสี่ยงสำหรับรายการเก่า และเติมเหตุการณ์ "fraud_review_requested" โดยไม่ต้องเขียนทับฐานข้อมูล
สังเกตสิ่งที่สิ่งนี้บังคับให้คุณทำ วิธีการแบบล็อกผลักให้คุณตั้งชื่อเหตุการณ์อย่างชัดเจน รักษาเสถียรภาพของพวกมัน และยอมรับว่าทีมและบริการหลายทีมจะขึ้นอยู่กับพวกมัน มันยังบังคับคำถามที่มีประโยชน์: แหล่งความจริงคืออะไร เหตุการณ์นี้หมายความว่าอย่างไรในระยะยาว เราทำอย่างไรเมื่อเราทำผิดพลาด
คุณค่าที่แท้จริงไม่ใช่บุคลิกของเครื่องมือ แต่มาจากการตระหนักว่าล็อกที่ใช้ร่วมกันสามารถกลายเป็นความทรงจำของระบบคุณ และความทรงจำคือสิ่งที่ทำให้ระบบเติบโตโดยไม่พังเมื่อคุณเพิ่มผู้บริโภคใหม่
คิวข้อความเหมือนบรรทัดงานที่ต้องทำสำหรับซอฟต์แวร์ของคุณ ผู้ผลิตใส่งานลงบรรทัด ผู้บริโภคหยิบงานถัดไป ทำงานนั้น แล้วงานหายไป ระบบมุ่งเน้นที่การให้แต่ละงานถูกจัดการหนึ่งครั้งโดยเร็วที่สุด
ล็อกต่างออกไป มันคือบันทึกเรียงลำดับของข้อเท็จจริงที่เกิดขึ้น เก็บไว้เป็นลำดับทนทาน ผู้บริโภคไม่ "เอา" เหตุการณ์ไป พวกเขาอ่านล็อกในจังหวะของตัวเอง และสามารถอ่านอีกครั้งในภายหลัง ใน Kafka event streaming ล็อกนี้เป็นแนวคิดแกนหลัก
วิธีจดจำความแตกต่างในทางปฏิบัติ:
การตั้งค่าการเก็บรักษาเปลี่ยนการออกแบบ ด้วยคิว ถ้าคุณต้องการฟีเจอร์ใหม่ที่ต้องพึ่งพาข้อความเก่า (วิเคราะห์ การตรวจฉ้อโกง การเล่นซ้ำหลังบั๊ก) คุณมักต้องเพิ่มฐานข้อมูลแยกต่างหากหรือเริ่มจับสำเนาเพิ่มที่อื่น ๆ ด้วยล็อก การเล่นซ้ำเป็นเรื่องปกติ: คุณสามารถสร้างมุมมองอนุพันธ์ใหม่โดยอ่านตั้งแต่ต้น (หรือจากจุดตรวจที่รู้จัก)
Fan-out ก็เป็นความแตกต่างสำคัญ ลองนึกภาพบริการเช็คเอาต์ปล่อย OrderPlaced ด้วยคิว คุณมักเลือกกลุ่มคนงานชุดหนึ่งให้ประมวลผล หรือทำซ้ำงานผ่านหลายคิว ด้วยล็อก บิลลิ่ง อีเมล สินค้าคงคลัง การทำดัชนีค้นหา และการวิเคราะห์สามารถอ่านสตรีมเหตุการณ์เดียวกันได้อย่างอิสระ แต่ละทีมเคลื่อนไหวด้วยจังหวะของตัวเอง และการเพิ่มผู้บริโภคใหม่ในภายหลังไม่จำเป็นต้องเปลี่ยนผู้ผลิต
ดังนั้นแบบจำลองทางความคิดตรงไปตรงมา: ใช้คิวเมื่อคุณย้ายงาน; ใช้ล็อกเมื่อคุณบันทึกเหตุการณ์ที่หลายส่วนของบริษัทอาจต้องอ่าน ทั้งตอนนี้หรือในอนาคต
การสตรีมเหตุการณ์พลิกคำถามเริ่มต้น แทนที่จะถามว่า “ฉันควรส่งข้อความนี้ไปหาใคร?” คุณเริ่มด้วยการบันทึกว่า “เพิ่งเกิดอะไรขึ้น?” ฟังดูเล็ก แต่เปลี่ยนวิธีที่คุณออกแบบระบบ
คุณเผยแพร่ข้อเท็จจริงเช่น OrderPlaced หรือ PaymentFailed และส่วนอื่น ๆ ของระบบตัดสินใจว่าจะตอบสนองอย่างไร เมื่อไร และอย่างไร
ด้วย Kafka event streaming ผู้ผลิตหยุดต้องมีรายการการผสานโดยตรง บริการเช็คเอาต์สามารถเผยแพร่เหตุการณ์เดียว โดยไม่ต้องรู้ว่าการวิเคราะห์ อีเมล การตรวจฉ้อโกง หรือบริการแนะนำในอนาคตจะใช้มันหรือไม่ ผู้บริโภคใหม่สามารถปรากฏได้ในภายหลัง บางตัวอาจถูกพักการใช้งาน และผู้ผลิตยังคงทำงานเหมือนเดิม
นี่เปลี่ยนวิธีการกู้คืนจากความผิดพลาดด้วย ในโลกที่ใช้ข้อความอย่างเดียว เมื่อผู้บริโภคพลาดหรือมีบั๊ก ข้อมูลมัก "หายไป" เว้นแต่คุณจะสร้างแบ็กอัพแบบกำหนดเอง ด้วยล็อก คุณแก้โค้ดแล้วเล่นซ้ำประวัติเพื่อสร้างสถานะที่ถูกต้องใหม่ นั่นมักดีกว่าการแก้ไขฐานข้อมูลด้วยมือหรือสคริปต์ครั้งเดียวที่ไม่มีใครเชื่อถือ
ในทางปฏิบัติ การเปลี่ยนแปลงปรากฏในไม่กี่วิธีที่เชื่อถือได้: คุณปฏิบัติต่อเหตุการณ์เป็นบันทึกทนทาน คุณเพิ่มฟีเจอร์โดยการสมัครแทนการแก้ผู้ผลิต คุณสามารถสร้างมุมมองอ่านใหม่ (ดัชนีค้นหา แดชบอร์ด) จากศูนย์ และคุณได้ไทม์ไลน์ที่ชัดเจนขึ้นของสิ่งที่เกิดขึ้นข้ามบริการ
การสังเกตการณ์ดีขึ้นเพราะล็อกเหตุการณ์กลายเป็นการอ้างอิงร่วม เมื่อเกิดปัญหา คุณสามารถตามลำดับธุรกิจ: ออร์เดอร์ถูกสร้าง สินค้าจอง การชำระเงินรีไทร การจัดส่งถูกกำหนด ไทม์ไลน์นั้นมักเข้าใจง่ายกว่าการกระจายของล็อกแอป เพราะมันมุ่งไปที่ข้อเท็จจริงทางธุรกิจ
ตัวอย่างชัดเจน: หากบั๊กในส่วนลดตั้งราคาผิดเป็นเวลาสองชั่วโมง คุณสามารถแก้ไขและเล่นซ้ำเหตุการณ์ที่เกี่ยวข้องเพื่อคำนวณยอดใหม่ อัปเดตใบแจ้งหนี้ และรีเฟรชการวิเคราะห์ คุณกำลังแก้ผลลัพธ์โดยการคำนวณซ้ำ ไม่ใช่เดาว่าจะแก้ตารางไหนด้วยมือ
คิวเรียบง่ายเป็นเครื่องมือที่เหมาะเมื่อคุณกำลังย้ายงาน ไม่ใช่สร้างบันทึกระยะยาว เป้าหมายคือส่งงานให้คนงาน รัน แล้วลืมมัน หากไม่มีใครจำเป็นต้องเล่นซ้ำอดีต ตรวจสอบเหตุการณ์เก่า หรือเพิ่มผู้บริโภคใหม่ในภายหลัง คิวช่วยให้ทุกอย่างเรียบง่ายกว่า
คิวโดดเด่นสำหรับงานแบ็กกราวด์: ส่งอีเมลยืนยัน ลงขนาดรูปหลังอัปโหลด สร้างรายงานประจำคืน หรือติดต่อ API ภายนอกที่ช้า ในกรณีเหล่านี้ ข้อความเป็นเพียงตั๋วงาน เมื่องานเสร็จ ก็ตั๋วก็หมดหน้าที่
คิวยังเหมาะกับรูปแบบความเป็นเจ้าของทั่วไป: กลุ่มผู้บริโภคหนึ่งกลุ่มรับผิดชอบงาน และบริการอื่น ๆ ไม่ได้อ่านข้อความเดียวกันโดยอิสระ
คิวมักเพียงพอเมื่อข้อเหล่านี้จริง:
ตัวอย่าง: สินค้าให้ผู้ใช้อัปโหลดรูปภาพ แอปเขียนงาน "resize image" ลงคิว Worker A หยิบงาน สร้างภาพย่อ เก็บแล้วเครื่องหมายงานเสร็จ หากงานรันสองครั้ง ผลลัพธ์เหมือนกัน (idempotent) ดังนั้นการส่งอย่างน้อยหนึ่งครั้งก็พอ ไม่มีบริการอื่นต้องอ่านงานนั้นภายหลัง
ถ้าความต้องการของคุณเริ่มเลื่อนไปทางข้อเท็จจริงที่ใช้ร่วมกัน (ผู้บริโภคหลายคน) การเล่นซ้ำ การตรวจสอบ หรือ "ระบบเชื่อว่าอะไรเมื่อสัปดาห์ก่อน?" นี่แหละที่ Kafka event streaming และแนวทางแบบล็อกเริ่มให้ผลคุ้มค่า
ระบบแบบล็อกคุ้มค่าเมื่อเหตุการณ์ไม่ใช่ข้อความครั้งเดียวและเริ่มเป็นประวัติที่ใช้ร่วมกัน แทนที่จะ "ส่งแล้วลืม" คุณเก็บบันทึกเรียงลำดับที่หลายทีมสามารถอ่านได้ทั้งตอนนี้และในอนาคต ตามจังหวะของตัวเอง
สัญญาณชัดเจนคือผู้บริโภคหลายราย เหตุการณ์เดียวเช่น OrderPlaced สามารถจ่ายให้บิลลิ่ง อีเมล การตรวจฉ้อโกง การทำดัชนีค้นหา และการวิเคราะห์ ด้วยล็อก แต่ละผู้บริโภคอ่านสตรีมเดียวกันอย่างอิสระ คุณไม่ต้องสร้างท่อแฟนเอาต์แบบกำหนดเองหรือประสานว่าใครได้ข้อความก่อน
อีกประโยชน์คือการตอบคำถาม "เรารู้ว่าอะไรตอนนั้น?" หากลูกค้าโต้แย้งการชาร์จ หรือคำแนะนำผิดพลาด ประวัติแบบ append-only ทำให้สามารถเล่นซ้ำข้อเท็จจริงตามที่มาถึงได้ แทรลตรวจสอบแบบนี้ยากที่จะต่อเติมเข้ากับคิวธรรมดาภายหลัง
คุณยังได้วิธีปฏิบัติในการเพิ่มฟีเจอร์โดยไม่ต้องเขียนใหม่ หากคุณเพิ่มหน้า "สถานะการจัดส่ง" เดือนต่อมา บริการใหม่สามารถสมัครและเติมย้อนหลังจากประวัติที่มีเพื่อสร้างสถานะ แทนการขอให้ระบบอื่นส่งเอ็กซ์พอร์ต
แนวทางแบบล็อกมักคุ้มค่าเมื่อคุณพบความต้องการอย่างน้อยหนึ่งข้อเหล่านี้:
รูปแบบทั่วไปคือสินค้าที่เริ่มจากออร์เดอร์และอีเมล ต่อมา ฝ่ายการเงินต้องการรายงานรายวัน ฝ่ายผลิตภัณฑ์ต้องการฟันเนล ฝ่ายปฏิบัติการต้องการแดชบอร์ดสด หากแต่ละความต้องการใหม่บังคับให้คุณคัดลอกข้อมูลผ่านท่อใหม่ ค่าใช้จ่ายจะเพิ่มอย่างรวดเร็ว ล็อกเหตุการณ์ร่วมช่วยให้ทีมต่อยอดจากแหล่งความจริงเดียว แม้ระบบและรูปร่างเหตุการณ์จะเปลี่ยนไป
การเลือกระหว่างคิวเรียบง่ายกับแนวทางแบบล็อกง่ายขึ้นเมื่อคุณถือมันเป็นการตัดสินใจด้านผลิตภัณฑ์ เริ่มจากสิ่งที่คุณต้องการให้เป็นจริงในหนึ่งปีข้างหน้า ไม่ใช่แค่สิ่งที่ใช้งานได้สัปดาห์นี้
แม็ปผู้เผยแพร่และผู้อ่าน เขียนว่าใครสร้างเหตุการณ์และใครอ่านตอนนี้ แล้วเพิ่มผู้บริโภคที่คาดว่าจะมาในอนาคต (analytics, การทำดัชนีค้นหา, การตรวจฉ้อโกง, การแจ้งเตือนลูกค้า) หากคุณคาดว่าหลายทีมจะอ่านเหตุการณ์เดียวกันอย่างอิสระ ล็อกเริ่มมีเหตุผล
ถามว่าคุณจะต้องอ่านประวัติซ้ำไหม ระบุให้ชัดเจนทำไม: เล่นซ้ำหลังบั๊ก เติมย้อนหลัง หรือลูกค้าที่อ่านช้าต่างระดับ คิวเหมาะกับการส่งงานครั้งเดียว ล็อกดีกว่าถ้าคุณต้องการบันทึกที่เล่นซ้ำได้
กำหนดว่า “เสร็จ” หมายถึงอะไร สำหรับบางเวิร์กโฟลว์ เสร็จคือ “งานรันแล้ว” (ส่งอีเมล ตัดขนาดรูป) สำหรับบางอย่าง เสร็จคือ “เหตุการณ์เป็นข้อเท็จจริงทนทาน” (สั่งซื้อถูกวาง การชำระเงินได้รับการอนุมัติ) ข้อเท็จจริงทนทานผลักคุณไปสู่ล็อก
เลือกความคาดหวังการส่งและตัดสินใจวิธีจัดการสำเนาซ้ำ การส่งแบบ at-least-once พบได้บ่อย ซึ่งหมายความว่าอาจมีสำเนาซ้ำ หากการซ้ำอาจก่อความเสียหาย (เช่น ชาร์ดบัตรซ้ำ) วางแผน idempotency: เก็บ ID เหตุการณ์ที่ประมวลผลแล้ว ใช้ข้อจำกัดเฉพาะ หรือนำการอัปเดตให้ปลอดภัยต่อการทำซ้ำ
เริ่มจากชิ้นบาง ๆ ที่ง่ายจะเข้าใจ หากคุณเลือก Kafka event streaming ให้เก็บ topic แรกแคบ ๆ ตั้งชื่อเหตุการณ์ชัดเจน และหลีกเลี่ยงการผสมเหตุการณ์ที่ไม่เกี่ยวข้อง
ตัวอย่างชัดเจน: หาก OrderPlaced จะป้อนข้อมูลให้การจัดส่ง การออกใบแจ้งหนี้ การสนับสนุน และการวิเคราะห์ในภายหลัง ล็อกให้แต่ละทีมอ่านในจังหวะของตัวเองและเล่นซ้ำเมื่อมีข้อผิดพลาด หากคุณต้องการเพียงคนงานพื้นหลังส่งใบเสร็จ คิวธรรมดามักพอ
จินตนาการร้านออนไลน์ขนาดเล็ก ในตอนแรกต้องการแค่รับออร์เดอร์ ตัดบัตร และสร้างคำขอจัดส่ง วิธีที่ง่ายที่สุดคือมีงานแบ็กกราวด์งานเดียวที่รันหลังเช็คเอาต์: “process order” มันเรียก API ชำระเงิน อัปเดตแถวออร์เดอร์ในฐานข้อมูล แล้วเรียกการจัดส่ง
สไตล์คิวนั้นใช้ได้ดีเมื่อมีเวิร์กโฟลว์ชัดเจน ผู้บริโภคเดียวชัดเจน และการรีไทรกับ dead letter ครอบคลุมความล้มเหลวส่วนใหญ่
มันเริ่มเจ็บเมื่อตลาดเติบโต ฝ่ายซัพพอร์ตต้องการอัปเดต "พัสดุของฉันอยู่ไหน" อัตโนมัติ ฝ่ายการเงินต้องการตัวเลขรายได้ประจำวัน ฝ่ายผลิตภัณฑ์ต้องการอีเมลลูกค้า ควรมีการตรวจฉ้อโกงก่อนจัดส่ง ด้วยงาน "process order" เดียว คุณจะต้องแก้คนงานซ้ำแล้วซ้ำเล่า เพิ่มสาขา และเสี่ยงต่อบั๊กใหม่ในกระแสหลัก
ด้วยแนวทางล็อก เช็คเอาต์ผลิตข้อเท็จจริงเล็ก ๆ เป็นเหตุการณ์ และแต่ละทีมสามารถต่อยอด ตัวอย่างเหตุการณ์ทั่วไปคือ:
OrderPlacedPaymentConfirmedItemShippedRefundIssuedการเปลี่ยนแปลงสำคัญคือความเป็นเจ้าของ บริการเช็คเอาต์เป็นเจ้าของ OrderPlaced บริการชำระเงินเป็นเจ้าของ PaymentConfirmed การจัดส่งเป็นเจ้าของ ItemShipped ต่อมา ผู้บริโภคใหม่สามารถปรากฏโดยไม่ต้องเปลี่ยนผู้ผลิต: บริการตรวจฉ้อโกงอ่าน OrderPlaced และ PaymentConfirmed เพื่อคำนวณความเสี่ยง บริการอีเมลส่งใบเสร็จ การวิเคราะห์สร้างฟันเนล และเครื่องมือซัพพอร์ตเก็บไทม์ไลน์ของสิ่งที่เกิดขึ้น
นี่แหละที่ Kafka event streaming ให้ผลคุ้มค่า: ล็อกเก็บประวัติไว้ ดังนั้นผู้บริโภคใหม่สามารถย้อนกลับและตามทันตั้งแต่ต้น (หรือจากจุดที่รู้จัก) แทนที่จะขอให้ทีมอัพสตรีมแต่ละทีมเพิ่ม webhook
ล็อกยังไม่แทนที่ฐานข้อมูลของคุณ คุณยังต้องฐานข้อมูลสำหรับสถานะปัจจุบัน: สถานะล่าสุดของออร์เดอร์ ข้อมูลลูกค้าจำนวนสินค้าคงคลัง และกฎธุรกรรม (เช่น "ห้ามจัดส่งหากการชำระเงินยังไม่ยืนยัน") มองว่าล็อกเป็นบันทึกการเปลี่ยนแปลงและฐานข้อมูลเป็นที่ที่คุณคิวรี่ว่า "อะไรเป็นความจริงตอนนี้"
การสตรีมเหตุการณ์ทำให้ระบบดูสะอาดขึ้น แต่ข้อผิดพลาดทั่วไปไม่กี่อย่างสามารถลบประโยชน์ไปได้อย่างรวดเร็ว ส่วนใหญ่เกิดจากการปฏิบัติต่อล็อกเหตุการณ์เหมือนรีโมตคอนโทรลมากกว่าบันทึก
กับดักบ่อยคือเขียนเหตุการณ์เป็นคำสั่ง เช่น "SendWelcomeEmail" หรือ "ChargeCardNow" นั่นทำให้ผู้บริโภคผูกมัดแน่นกับเจตนา เหตุการณ์ทำงานได้ดีกว่าเมื่อเป็นข้อเท็จจริง: "UserSignedUp" หรือ "PaymentAuthorized" ข้อเท็จจริงทนทานและนำกลับมาใช้ซ้ำได้ง่าย
สำเนาซ้ำและการรีไทรเป็นแหล่งความเจ็บปวดถัดมา ในระบบจริง ผู้ผลิตรีไทรและผู้บริโภคประมวลผลซ้ำ หากคุณไม่วางแผนไว้ คุณจะเจอการชาร์จซ้ำ อีเมลซ้ำ และตั๋วซัพพอร์ตที่โกรธ การแก้ไม่ใช่เรื่องแปลกประหลาด แต่ต้องทำอย่างตั้งใจ: ตัวจัดการที่ idempotent, ID เหตุการณ์ที่คงที่ และกฎธุรกิจที่ตรวจจับว่า "ใช้แล้ว"
กับดักทั่วไป:
สคีมาและการวางเวอร์ชันต้องให้ความสนใจพิเศษ แม้จะเริ่มด้วย JSON คุณก็ยังต้องสัญญาที่ชัดเจน: ฟิลด์ที่จำเป็น ฟิลด์ทางเลือก และวิธีการเปิดตัวการเปลี่ยนแปลง การเปลี่ยนเล็ก ๆ เช่นการเปลี่ยนชื่อฟิลด์อาจทำให้การวิเคราะห์ การเรียกเก็บเงิน หรือแอปมือถือที่อัปเดตช้าพังได้
กับดักอีกอย่างคือการแยกสตรีมมากเกินไป ทีมมักสร้างสตรีมใหม่สำหรับทุกฟีเจอร์ เดือนต่อมาไม่มีใครตอบได้ว่า "สถานะปัจจุบันของออร์เดอร์คืออะไร" เพราะเรื่องราวกระจายอยู่หลายที่
การสตรีมเหตุการณ์ไม่ได้ยกเลิกความต้องการแบบโมเดลข้อมูลที่มั่นคง คุณยังต้องฐานข้อมูลที่เป็นความจริงปัจจุบัน ล็อกเป็นประวัติ ไม่ใช่แอปพลิเคชันทั้งหมดของคุณ
หากคุณยังลังเลระหว่างคิวกับ Kafka event streaming ให้เริ่มด้วยการเช็กด่วน สิ่งเหล่านี้จะบอกว่าคุณต้องการแค่การส่งงานระหว่างคนงาน หรือบันทึกที่คุณจะใช้ซ้ำเป็นปี
ถ้าคุณตอบ "ไม่" ต่อการเล่นซ้ำ, "มีผู้บริโภคเดียว" และ "ข้อความมีอายุสั้น" คิวพื้นฐานมักเพียงพอ หากตอบ "ใช่" ต่อการเล่นซ้ำ ผู้บริโภคหลายราย หรือการเก็บรักษานาน แนวทางแบบล็อกมักคุ้มค่าตรงที่มันเปลี่ยนสตรีมข้อเท็จจริงให้เป็นแหล่งร่วมที่ระบบอื่น ๆ สามารถสร้างต่อได้
แปลงคำตอบเป็นแผนเล็ก ๆ ที่ทดสอบได้
OrderPlaced, PaymentAuthorized, OrderShipped) แล้วจดว่าใครเป็นผู้เผยแพร่และใครเป็นผู้บริโภคorderId) และจดว่า "ลำดับที่ถูกต้อง" หมายถึงอะไรถ้าคุณกำลังสร้างต้นแบบอย่างรวดเร็ว คุณสามารถร่างโฟลว์เหตุการณ์ในโหมดวางแผนของ Koder.ai และทำซ้ำการออกแบบก่อนล็อกชื่อเหตุการณ์และกฎ retry เมื่อ Koder.ai รองรับการส่งออกซอร์สโค้ด สแนปช็อต และการย้อนกลับ มันเป็นวิธีปฏิบัติที่ดีในการทดสอบชิ้นเดียวของผู้ผลิต–ผู้บริโภคและปรับรูปร่างเหตุการณ์โดยไม่เปลี่ยนการทดลองเริ่มต้นให้กลายเป็นหนี้ในโปรดักชัน
คิวเหมาะกับ ตั๋วงาน ที่ต้องการให้ทำแล้วลืม (ส่งอีเมล ตัดขนาดรูป รันงาน) ส่วนล็อกเหมาะกับ ข้อเท็จจริง ที่ต้องการเก็บไว้และให้หลายระบบอ่านหรือเล่นซ้ำในภายหลัง (เช่น OrderPlaced, PaymentAuthorized, RefundIssued)
คุณจะรู้สึกได้เมื่อฟีเจอร์ใหม่ทุกชิ้นต้องแก้หลายการเชื่อมต่อ และการดีบักกลายเป็นคำถามว่า “ใครเขียนค่านี้?” โดยไม่มีไทม์ไลน์ชัดเจน ล็อกช่วยได้เพราะมันเป็นบันทึกร่วมที่คุณตรวจสอบและเล่นซ้ำได้ แทนที่จะเดาจากสถานะในฐานข้อมูลที่กระจัดกระจาย
เมื่อคุณต้องการเทรลตรวจสอบและเล่นซ้ำ: แก้บั๊กโดยประมวลผลประวัติใหม่ เติมข้อมูลฟีเจอร์จากข้อมูลเก่า ตรวจสอบว่า “ตอนนั้นเรารู้เรื่องอะไรบ้าง?” หรือต้องรองรับผู้บริโภคหลายราย (billing, analytics, support, fraud) โดยไม่ต้องเปลี่ยนผู้ผลิตทุกครั้ง
เพราะระบบล้มเหลวในแบบที่ซับซ้อน: การรีไทร การหมดเวลา บางส่วนล้มเหลว และการแก้ด้วยมือ หากแต่ละบริการอัปเดตบริการอื่นโดยตรง พวกมันอาจไม่เห็นด้วยกันเกี่ยวกับสิ่งที่เกิดขึ้น ประวัติแบบ append-only ให้ลำดับเหตุการณ์เดียวที่ใช้เป็นเกณฑ์อ้างอิง แม้ผู้บริโภคบางตัวจะล้มเหลวและตามมาทีหลัง
ออกแบบเหตุการณ์เป็น ข้อเท็จจริงนิ่ง (past tense) ที่อธิบายสิ่งที่เกิดขึ้นแล้ว เช่น:
OrderPlaced แทน ProcessOrderPaymentAuthorized แทน ChargeCardNowถือว่าซ้ำจะเกิดขึ้นได้ (การส่งแบบ at-least-once ถูกใช้บ่อย) ทำให้ผู้บริโภคปลอดภัยต่อการลองใหม่โดย:
กฎพื้นฐาน: ความถูกต้องมาก่อน ความเร็วทีหลัง
ยอมรับการเปลี่ยนแปลงแบบเพิ่ม (additive): เก็บฟิลด์เดิมไว้ เพิ่มฟิลด์ใหม่เป็นทางเลือก และหลีกเลี่ยงการเปลี่ยนชื่อ/ลบฟิลด์ที่ผู้บริโภคเก่าย่อมพึ่งพา หากต้องการเปลี่ยนแบบทำลาย (breaking) ให้เวอร์ชันเหตุการณ์ (หรือหัวข้อ/สตรีม) แล้วย้ายผู้บริโภคอย่างระมัดระวัง แทนที่จะอัปเดต JSON แบบสุ่ม
เริ่มจากชิ้นบาง ๆ แบบครบวงจร:
OrderPlaced → ส่งอีเมลยืนยัน)orderId หรือ userId)ไม่เลย ล็อกไม่ใช่ตัวแทนของฐานข้อมูลปัจจุบัน คุณยังต้องมีฐานข้อมูลสำหรับสถานะล่าสุดและธุรกรรม ("อะไรเป็นความจริงตอนนี้") ใช้ล็อกเป็นประวัติและแหล่งข้อมูลสำหรับการสร้างมุมมองอนุพันธ์ (ตารางวิเคราะห์ ดัชนีค้นหา ไทม์ไลน์)
โหมดวางแผนช่วยแม็ปผู้เผยแพร่/ผู้บริโภค กำหนดชื่อเหตุการณ์ และตัดสินใจเรื่อง idempotency กับการเก็บรักษาก่อนเขียนโค้ดจริง จากนั้นสร้างผู้ผลิต–ผู้บริโภคชิ้นเล็ก ๆ ถ่ายสแนปช็อต และย้อนกลับได้ง่ายขณะปรับรูปร่างเหตุการณ์ เมื่อตั้งค่าเสถียรแล้ว คุณสามารถส่งออกซอร์สโค้ดและดีพลอยเหมือนบริการอื่น ๆ ได้
UserEmailChangedUpdateEmailถ้ามีการเปลี่ยนแปลง ให้เผยแพร่เหตุการณ์ใหม่ที่ระบุความจริงล่าสุด แทนการแก้ไขเหตุการณ์เดิม
พิสูจน์วงจรเดียวก่อนขยายไปยังเหตุการณ์และทีมอื่น ๆ