การเปลี่ยนผ่านจากข้อมูลเชิงสัมพันธ์ไปสู่อีเวนต์
(event-driven.io)- โมเดล CRUD เชิงสัมพันธ์แสดงโครงสร้างการจัดเก็บได้ดี แต่มีแนวโน้มที่จะเขียนทับ กระบวนการทางธุรกิจ ทำให้ติดตามได้ยากว่าเกิดอะไรขึ้นจริงในระบบ
- Event Sourcing จะบันทึก อีเวนต์ที่เปลี่ยนแปลงไม่ได้ ซึ่งเกิดขึ้นหลังการทำงานแต่ละครั้งไว้เป็น Event Stream และในการตัดสินใจภายหลังจะอ่านรายการนั้นเพื่อประเมินสถานะปัจจุบัน
- การทำโมเดลเริ่มจากการหาอีเวนต์ก่อน จากนั้นจึงเชื่อมโยง คำสั่ง (command) ซึ่งเป็นเจตนาการกระทำของผู้ใช้กับกฎทางธุรกิจ เพื่อทำความเข้าใจกระบวนการ
- เมื่อค้นหาอีเวนต์ที่เป็นไปได้จากข้อมูลเชิงสัมพันธ์เดิม ควรพิจารณาคอลัมน์สถานะ คอลัมน์วันที่ การอนุญาตให้เป็น nullable และความสัมพันธ์แบบ 1:N แต่การคิดว่าสามารถกู้คืน ประวัติที่สมบูรณ์ ได้จากค่าสถานะเพียงอย่างเดียวนั้นอันตราย
- เมื่อต้องย้ายข้อมูลที่เหลือเพียงสถานะสุดท้าย แนวทางที่เป็นจริงกว่าคือเริ่มด้วยอีเวนต์ import ที่ระบุชัดเจน เช่น Order Imported แทนการฝืนสร้างอีเวนต์ในอดีตขึ้นมาใหม่ และตรวจสอบซ้ำในสภาพแวดล้อมที่ปลอดภัย
มองจากข้อมูล CRUD ไปสู่โมเดลที่มีอีเวนต์เป็นศูนย์กลาง
- โมเดลข้อมูลเชิงสัมพันธ์แสดงให้เห็นว่าเก็บข้อมูลอะไรไว้ แต่ยากที่จะเข้าใจว่า เกิดอะไรขึ้น ภายในระบบ และกระบวนการต่าง ๆ โต้ตอบกันอย่างไร
- วิธี CRUD แบบเดิมอาจทำให้สูญเสียข้อมูลทางธุรกิจที่สำคัญจากการเขียนทับข้อมูล
- Event Sourcing ให้ความสำคัญกับ คุณภาพของข้อมูล มากกว่าขนาดพื้นที่จัดเก็บ โดยบันทึกข้อเท็จจริงที่เกิดขึ้นหลังการทำงานแต่ละครั้งเป็นอีเวนต์
โมเดลพื้นฐานของ Event Sourcing
- อีเวนต์คือ ข้อเท็จจริง เกี่ยวกับสิ่งที่เกิดขึ้นแล้ว และเป็นข้อมูลที่เปลี่ยนแปลงไม่ได้หลังจากถูกบันทึก
- Event Stream คือรายการที่เก็บทุกสิ่งที่เกิดขึ้นกับเรคคอร์ดหนึ่งรายการตามลำดับ
- ไม่สามารถแก้ไขอีเวนต์ในอดีตได้ แต่สามารถเพิ่มอีเวนต์ใหม่ต่อท้ายเพื่อแก้ไขข้อผิดพลาดก่อนหน้าได้
- เมื่อต้องตัดสินใจ จะอ่านและตรวจสอบรายการอีเวนต์เพื่อประเมินสถานะปัจจุบันและการกระทำถัดไป
ลำดับการทำโมเดลกระบวนการ
- การทำโมเดลเริ่มจาก การค้นพบอีเวนต์ ก่อน
- จากนั้นจึงค้นหาคำสั่ง (command) และนิยามว่าเป็นเจตนาที่ต้องการทำการกระทำใด
- สุดท้ายจึงจัดระเบียบ กฎทางธุรกิจ
- อีเวนต์กลายเป็นแกนกลางที่ช่วยให้ฝ่ายเทคนิคและฝ่ายธุรกิจเข้าใจกระบวนการร่วมกัน
- สามารถทำความเข้าใจกระบวนการโดยดูอีเวนต์ คำสั่ง และกฎร่วมกันได้ เช่น EventStorming ของ Alberto Brandolini
หาอีเวนต์ที่เป็นไปได้จากข้อมูลเชิงสัมพันธ์เดิม
-
1. พิจารณาคอลัมน์สถานะ
- ค่าของคอลัมน์ status อาจสะท้อนขั้นตอนในวงจรชีวิตของข้อมูล
- หากคำสั่งซื้อมีสถานะอย่าง initiated, shipped, paid แต่ละสถานะอาจเป็นตัวเลือกของอีเวนต์ Order Initiated, Order Shipped, Order Paid ได้
- อย่างไรก็ตาม ค่าสถานะอาจเป็นการตีความที่ทำให้กระบวนการทางธุรกิจแบนราบลง จึงไม่ควรถือว่าสมบูรณ์
- ควรหลีกเลี่ยงการตั้งชื่ออีเวนต์ตามการทำงานแบบ CRUD เช่น Order Created, Order Updated, Order Deleted
- State Obsession ถูกเสนอว่าเป็นแนวทางที่ควรหลีกเลี่ยง
-
2. ตรวจสอบคอลัมน์วันที่
- คอลัมน์วันที่อาจบอก ช่วงเวลาการเกิดขึ้นที่สำคัญ ในวงจรชีวิตของกระบวนการได้
- CreatedDate และ ModifiedDate ไม่ได้ให้ข้อมูลมากนัก แต่ ShipmentDate, DeliveryDate, OrderPlacementDate เป็นเบาะแสที่ดีกว่า
- ตัวอย่าง:
- ShipmentDate อาจเป็นเบาะแสในการนำอีเวนต์ Order Shipped มาใช้
- OrderPlacementDate บ่งชี้ว่า Order Placed อาจเป็นชื่อที่ดีกว่า Order Initiated
- DeliveryDate แสดงให้เห็นว่าอาจต้องมีอีเวนต์ Order Delivered
- เบาะแสเหล่านี้ควรตรวจสอบกับผู้เชี่ยวชาญโดเมนเพื่อให้สอดคล้องกับกระบวนการทางธุรกิจจริง
-
3. วิเคราะห์ว่าคอลัมน์เป็น nullable หรือไม่
- คอลัมน์ non-nullable คือข้อมูลที่ต้องมีเสมอ
- คอลัมน์ nullable อาจเป็นข้อมูลที่ถูกเติมภายหลังจากการทำงานอื่น หรือเป็นค่าทางเลือก
- หากเป็นคอลัมน์ที่จำเป็นใน Ordering Process ข้อมูลนั้นก็ควรรวมอยู่ในอีเวนต์ Order Initiated แรกด้วย
- อีเวนต์ประเภทเดียวไม่จำเป็นต้องเป็นจุดเริ่มต้นของ stream เสมอไป และอีเวนต์เริ่มต้นอาจมีได้หลายรายการ
-
4. หาเทเบิลที่มีความสัมพันธ์ 1:N จำนวนมาก
- หากต้องการหาขอบเขตของ stream สามารถเริ่มจากเทเบิลที่มี ความสัมพันธ์ 1:N จำนวนมาก
- เทเบิลที่มีความสัมพันธ์ฝั่ง “one” จำนวนมากจะเป็นตัวเลือกของประเภท stream
- ต้องพิจารณาเชิงตรรกะด้วยว่าข้อมูลสามารถมีอยู่แยกจากกันได้หรือไม่
- shipment อาจเป็นกระบวนการที่แยกจาก order
- order line มักมีอยู่ได้ยากหากไม่มี order
- ระหว่างการถกเถียงเรื่องขอบเขต อาจค้นพบอีเวนต์เพิ่มขึ้นและขยายความเข้าใจกระบวนการได้
อย่าสร้างอีเวนต์เท็จระหว่างการไมเกรชัน
- ข้อมูลเชิงสัมพันธ์เป็น สถานะสุดท้ายที่ถูกทำให้แบนราบ ดังนั้น หากพยายามย้อนเดาอีเวนต์ย่อยในอดีตที่เกิดขึ้นจริงจากสถานะนั้นเพียงอย่างเดียว อาจล้มเหลวหรือไม่แม่นยำ
- แทนที่จะฝืนสร้างอีเวนต์ย่อยในอดีต ควรให้อีเวนต์ Order Imported อย่างชัดเจนที่บรรจุสถานะปัจจุบันทั้งหมดและโค้ดสำหรับตีความ
- อีเวนต์ import แสดงให้เห็นอย่างชัดเจนว่าข้อมูลเข้ามาด้วยวิธีใด และอาจสำคัญต่อการแก้ปัญหาและการวินิจฉัย
ตรวจสอบด้วยโปรโตไทป์
- การไมเกรชันควรทดลองด้วย โปรโตไทป์ ในสภาพแวดล้อมที่ปลอดภัย และตรวจสอบว่าโมเดลทำงานจริงอย่างไร
- ควรเปรียบเทียบผลลัพธ์กับค่าที่คาดหวังและปรับแก้ซ้ำ ๆ
- จำเป็นต้องใช้แนวทางที่ไม่เร่งรีบ ไม่สูญเสียข้อมูลเดิม และปรับปรุงโมเดลต่อไปโดยอาศัยข้อมูลนั้น
- กลยุทธ์ทั่วไปในการย้ายจากข้อมูลเชิงสัมพันธ์ไปสู่รูปแบบเอกสารยังเชื่อมโยงกับ General strategy for migrating relational data to document-based ด้วย
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
2c: หากส่วนอื่นของแอปก็ต้องใช้ PostgreSQL อยู่แล้ว ก็ควรเก็บข้อมูลอีเวนต์ไว้ใน PostgreSQL + เครื่องมือรายงานแบบ FOSS (เช่น Apache Superset, Metabase) และใช้งานให้ได้ราว ๆ 2TB
หลังจากนั้นค่อยตัดสินใจว่าจำเป็นต้องเก็บข้อมูลทั้งหมด 2TB ไว้แบบออนไลน์หรือไม่ หรือมีแค่ข้อมูลสรุปรายวัน/รายชั่วโมงก็พอ หากเป็นกรณีหลัง ใช้ PostgreSQL ต่อไปก็เพียงพอแล้ว[1]
ลูกค้ารายหนึ่งจัดการข้อมูลขนาด 10TB+ ด้วยอีเวนต์ 1,500 รายการต่อวินาที ขนาด 600 ไบต์ต่อเรคอร์ด (80GB ต่อวันก่อนทำดัชนี) โดยเก็บข้อมูลรายละเอียดไว้แบบออนไลน์เพียง 2 วัน ที่เหลือสรุปข้อมูล แล้วส่งรายละเอียดไปยัง S3 เพื่อให้ยังคิวรีต่อด้วย Athena SQL ได้[2]
ต้นทุนรวมทั้งหมด รวมถึงพอร์ทัลรายงานสำหรับลูกค้า ต่ำกว่า 2,000 ดอลลาร์ และบน AWS RDS แบบ multi-AZ พร้อม failover อัตโนมัติ (db.m7g.2xlarge) ก็รองรับทั้งการ insert และคิวรีรายงาน โดยโหลดต่ำกว่า 2% ทีมธุรกิจสร้างแผนภูมิและกราฟเองได้ จึงใช้วิศวกร 1 คนดูแลไม่ถึง 5 ชั่วโมงต่อเดือน
ถ้าใช้เครื่องมือ proprietary กราฟบางอย่างอาจ “มีมาให้ในตัว” แต่ถ้าใช้ pgsql ข้อมูลจะอยู่ที่เดียว มีระบบเดียวที่ต้องเรียนรู้ ระบบเดียวที่ต้องดูแลให้ออนไลน์/ทำ replication/แบ็กอัป/กู้คืน ระบบเดียวที่ต้องรักษาความปลอดภัย/ขยายระบบ เวนเดอร์เดียวที่ต้องจัดการ และมีวิศวกรหลายล้านคนที่รู้จักระบบนี้
ในระบบอย่าง Preset หรือ Metabase การสร้างกราฟ 12 รายการใช้เวลาแค่ชั่วโมงเดียว และคนที่ไม่ใช่สายเทคนิคก็ทำได้
ขอเสริมว่าแม้ผมจะมีอคติอยู่บ้าง แต่ผมเห็นฐานข้อมูลและระบบรายงานเกิดขึ้นแล้วหายไปมานานกว่า 20 ปี ส่วน PostgreSQL รุ่นเก๋าที่ดีนั้นก็ดีขึ้นทุกปี
https://instances.vantage.sh/aws/rds/db.m7g.2xlarge?region=u...
[1] หากจำเป็นจริง ๆ ก็มีระบบที่เข้ากันได้กับ PostgreSQL สำหรับขยายเพิ่มได้ด้วย Aurora ขยายได้ราว 3~5 เท่า, TimescaleDB ราว 10 เท่า, CitusDB ราว 10 เท่า+ แต่ละตัวต้องแลกกับความไม่เป็นมาตรฐานเล็กน้อย จึงไม่แนะนำก่อนจะจำเป็นจริง ๆ
[2] แดชบอร์ดรายงานสำหรับลูกค้าต้องตอบสนองภายในไม่ถึง 1 วินาที ซึ่ง PostgreSQL ทำได้โดยคิวรีตารางสรุปที่ทำดัชนีไว้ ส่วน Athena ตอบสนองได้ราว 1~2 วินาทีด้วยการสแกนแบบขนาน
ให้เก็บ snapshot ของข้อมูลก่อนบันทึก มีสคริปต์สำหรับระบุและรวบรวมลำดับอีเวนต์เฉพาะ จากนั้นให้คนตรวจทาน แล้วค่อยปรับใช้ผลของลอจิกใหม่ย้อนหลังแบบเป็นชุดใหญ่ก็ได้
เครื่องมืออย่าง https://django-simple-history.readthedocs.io/en/latest/ เป็นวิธีแก้ปัญหาที่เรียบง่ายและพอเชื่อถือได้ในระดับหนึ่งสำหรับสร้างตาราง audit และถ้าต้อง audit ไปถึงการเข้าถึงฐานข้อมูลโดยตรง ก็เพิ่ม Postgres trigger ได้
ในเชิงทฤษฎีผมชอบ event sourcing แต่ในทางปฏิบัติ boilerplate ที่จำเป็นสำหรับการเพิ่ม flow แบบ CRUD ใหม่ หรือสำหรับการปล่อย intervention/hotfix ที่สตาร์ทอัพช่วงต้นถึงกลางมักต้องทำเมื่อเจอสถานการณ์ไม่คาดคิด ให้ได้รวดเร็วและมั่นคงนั้นมีมากเกินไป
หากไม่ใช่งานอย่างการทำ rail สำหรับประมวลผลการชำระเงิน event sourcing อาจไม่ใช่ตัวเลือกที่เหมาะ
ใน https://news.ycombinator.com/item?id=17817375 (2018) ก็มีบทสนทนาดี ๆ เกี่ยวกับข้อเสียของ event sourcing
ปัญหาเดียวของ PostgreSQL คือฝั่ง insert มี ปัญหาด้าน scalability ที่น่าสนใจอยู่ โดยทั่วไปจะแนะนำให้มี queue คั่นระหว่าง event source กับ DB
{id:uuid,created_at:timestamptz,data:jsonb}หรือเปล่าโดยเฉพาะเมื่อโครงสร้างอีเวนต์หลากหลายและนิยามอีเวนต์เปลี่ยนไปเรื่อย ๆ การใช้ความสามารถ JSONB index ให้ได้เต็มที่ดูจะยาก
น่าจะต้องศึกษาเอกสารนี้ให้มากขึ้น: https://www.postgresql.org/docs/current/datatype-json.html#J...
เมื่อก่อนทีมเคยพิจารณา event sourcing อย่างจริงจัง แต่สำหรับผมมันดูเหมือน วิธีแก้ที่กำลังมองหาปัญหา
มันอาจใช้กับเราได้ก็จริง แต่ประโยชน์ไม่ได้ชัดเจนทันที และความเสี่ยงกับการลองผิดลองถูกที่จะได้จากการนำแนวทางใหม่เข้ามา ก็ดูไม่ใช่สิ่งที่ดีที่สุดสำหรับโปรเจกต์หรือบริษัท สุดท้ายจึงเลิกไป
อาจเป็นการตัดสินใจแบบเครื่องมือที่พลาดโอกาสเรียนรู้ก็ได้ แต่ผมก็ไม่เสียใจที่ไม่ได้มุดลงไปในโพรงกระต่ายนั้น ทั้งที่ไม่ได้มีหมาจิ้งจอกไล่ตามอยู่ข้างหลัง
“ปัญหา” ที่วิธีแก้นี้แก้อยู่ก็คือสิ่งนั้นแหละ
แต่ในกรณีส่วนใหญ่ ใช้ฐานข้อมูลทั่วไปแล้วเก็บประวัติการเปลี่ยนแปลงในอดีตไว้ในตารางเสริมก็พอ แบบนั้นฐานข้อมูลหลักก็จะทำงานคล้าย materialized view แบบหนึ่ง
ก็ไม่ได้มีอะไรให้บ่นมาก และผมก็ไม่ได้คิดถึงขั้นว่าเป็นทางเลือกที่ผิด แต่ปัญหาเกิดขึ้นในวิธีจัดการการเปลี่ยนแปลงของ data model
ดูเหมือนว่าวิธีเก็บข้อมูลส่วนใหญ่ตามไม่ทันวิธีที่ซอฟต์แวร์สมัยนี้ถูกสร้างขึ้น และสิ่งอย่าง event กับ queue ก็เป็นผลจากการเอาฟีเจอร์ที่ต้องการมาวางทับบนระบบเดิม
ทุกวันนี้ความสัมพันธ์ของข้อมูลจำนวนมากเกิดขึ้นระหว่างหลายบริการ กล่าวคืออยู่นอกฐานข้อมูล เพราะสภาพแวดล้อม IT สมัยใหม่ของหลายองค์กรมีหน้าตาแบบนั้น
มี master data ภายในที่รองรับหลายทีมธุรกิจ และเพื่อทำให้งานเรียบง่ายขึ้นก็ต้องโต้ตอบกับระบบ IT และแอปพลิเคชันมากกว่า 300 รายการ
ถ้าใช้ microservices การรักษา business logic และ data model ให้เรียบร้อยนั้นง่ายขึ้น แต่แลกกับการต้องจัดการ event, queue, สถานะข้อมูล และ dependent storage ด้วย ซึ่งตอนนี้มันซับซ้อนเกินไป
ผมชอบ SQL นะ แต่พูดตรง ๆ ระบบที่เราสร้างกันทุกวันนี้ดูเหมือนว่าแทบจะใส่ไว้ใน SQLite ก็เพียงพอแล้ว
สิ่งที่มักตกหล่นในการถกเถียงแบบนี้คือ event-driven architecture เหมาะเมื่อไร
พูดสั้น ๆ ถ้าลูกค้าทำอะไรบางอย่างและคาดหวังการตอบกลับ นั่นไม่ใช่ event-driven แต่เป็น request/response ธรรมดา
event-driven คือกรณีที่มีบางอย่างเกิดขึ้นนอกช่องทางหลัก เช่น สถานการณ์ที่ push โค้ดขึ้น GitHub แล้วทำให้ build ถูก trigger
ในตัวอย่างนี้ การ refresh หน้าเพื่อดูโค้ดที่อัปเดตแล้วคือ request/response แต่ CI build ที่เข้า queue คือ event-driven
หวังว่าจะช่วยได้
ใน event sourcing หรือ event-driven ก็สามารถสร้าง request-response, inline, blocking, cyclic flow ได้
ในทางกลับกัน แม้ไม่มี event sourcing หรือ event-driven ก็ยังทำ asynchronous ได้ดีด้วยวิธีอย่าง worker, queue, actor, multithreading
การทำโมเดล domain event มีประโยชน์ในการอธิบายปัญหาที่ต้องการแก้ร่วมกับ domain expert และอาจเหมาะกับการบันทึกไว้ในเอกสารตอนวางแผนโซลูชัน
ถ้าจะนำระบบที่ให้ audit trail ของ state machine ที่ดำรงอยู่ระยะยาวไปใช้งานจริง ก็น่าจะใช้เครื่องมืออย่าง Temporal.io หรือ durable functions มากกว่า
เครื่องมือเหล่านี้ใช้ event sourcing สำหรับ persistence ภายใน และมี programming model ที่เพิ่มข้อจำกัดต่างกันให้กับโค้ดที่ orchestrate ฟังก์ชัน (workflows) กับโค้ดที่โต้ตอบกับโลกจริง (activities) เพื่อบังคับให้คิดเรื่อง deduplication และ idempotency
อยากฟังข้อเสนอแนะเกี่ยวกับวิธีข้ามปัญหานี้
แนวคิดน่าสนใจ แต่บทความอธิบายวิธีการทำงานได้ไม่ค่อยดี
อยากรู้ว่าจะสร้างสถานะปัจจุบันขึ้นมาใหม่จาก event stream ได้อย่างมีประสิทธิภาพอย่างไร และจะโมเดล event stream ในฐานข้อมูลอย่างไร
https://www.youtube.com/watch?v=gG6DGmYKk4I
https://www.youtube.com/watch?v=jnDchr5eabI
https://www.youtube.com/watch?v=ArcypYS5XBQ
https://www.youtube.com/watch?v=uODSwR2CIV4
ยังดูแลตัวอย่างไว้บน GitHub ด้วย
https://github.com/oskardudycz/EventSourcing.NetCore
https://github.com/oskardudycz/EventSourcing.NodeJS
https://github.com/oskardudycz/EventSourcing.JVM
อย่างแรกคือใช้ฐานข้อมูลที่ออกแบบมาสำหรับงานประเภทนี้ เช่น Google BigQuery, Amazon Redshift, ClickHouse เป็นต้น
ข้อมูลปัจจุบันทั้งหมดโดยแก่นแท้แล้วเป็นการ aggregate แบบหนึ่ง กล่าวอีกอย่างคือเหมือน query แบบ group-by บนฐานข้อมูล event
เมื่อมี event ก็สามารถสร้างสถานะปัจจุบันหรือสถานะในอดีตขึ้นมาใหม่ทางเทคนิคได้ด้วย query แบบ aggregate จึงสมเหตุสมผล
อย่างที่สองคือเปลี่ยนชื่อ relational store ให้เป็น ชั้น cache ที่อยู่ข้างระบบ event
ในเชิงฟังก์ชันก็เป็นสิ่งเดียวกัน แต่จะไม่ทำให้คนที่หมกมุ่นว่าทุกอย่างต้องขับเคลื่อนด้วย event เกิดสัญญาณเตือน
สถาปัตยกรรมที่บทความอธิบายนั้นมีอยู่จริง เพียงแต่มันซับซ้อนอย่างมหาศาล บริการที่ใช้ประโยชน์จากมันจึงมักทำงานที่เจาะจงมาก ๆ ลองนึกถึง Google Analytics, Datadog, Splunk เป็นต้น
ให้สร้างสถานะที่ต่างกันในระบบที่ต่างกันตามความต้องการที่ต่างกันก็ได้
สมมติว่าสร้างระบบช็อปปิง เมื่อมีการซื้อและลูกค้า บริการหนึ่งอาจอ่าน event แล้วสร้างตาราง relational เพื่อวัตถุประสงค์ทางการเงิน
อีกบริการหนึ่งอ่าน event แล้วสร้าง key-value store ของข้อมูลลูกค้า และบริการที่สามอาจขับเคลื่อนบริการ OpenSearch สำหรับการค้นหาสินค้า
event stream คือรายการ ถ้าใช้สิ่งที่เหมาะกับวัตถุประสงค์อย่าง Kafka ก็จะเป็นหลายรายการ กล่าวคือ topic และ partition เป็นต้น
แต่เรื่องนั้นก็แก้ได้ภายใน relational model เช่นกัน
นี่คือความแตกต่างระหว่าง บนลงล่าง vs ล่างขึ้นบน หรือแบบเฉพาะทาง vs แบบทั่วไป
แบบบนลงล่างคือเริ่มจากโดเมนธุรกิจ แล้ว map การ implement ลงบนเทคโนโลยี·เครื่องมือ·vendor ที่มีให้ใช้
แบบล่างขึ้นบนคือเริ่มจากเทคโนโลยี·เครื่องมือ·vendor ที่มีให้ใช้ แล้วประกอบมันเข้าด้วยกันเพื่อสร้างโซลูชันที่ทำงานได้
แบบเฉพาะทางมี DDD, CQRS/ES, Sagas, TBUI (UI แบบอิง/ขับเคลื่อนด้วยงาน), GraphQL, algebraic data types เป็นต้น
แบบทั่วไปมี RDBMS, CRUD, REST, ธุรกรรม ACID, CDC, UI แอดมินทั่วไป, no-code/low-code, type แบบจำกัด/ทั่วไป เป็นต้น
ผมจะใช้ relational data แบบเก่า ๆ ที่ดีต่อไป
เห็นด้วยกับสถาปัตยกรรม event-based แต่บทความนี้ดูเหมือนจะสื่อประเด็นหลักได้ลำบาก
ผมจะเน้นที่ความแตกต่างระหว่างความสัมพันธ์ของข้อมูลกับ การกระทำทางธุรกิจ
เมื่อเริ่มคิดในมุมของการกระทำและกิจกรรมทางธุรกิจ การเคลื่อนออกจาก operational relational data store จะชัดเจนขึ้นมาก
event sourcing มีคุณสมบัติดี ๆ หลายอย่าง จึงน่าสนใจ
แต่ก็ยังต้องการ relation อยู่ไม่ใช่หรือ? ถ้าอย่างนั้น relation นั้น implement อย่างไร?
ถ้าคำตอบคือ “ทั้งหมดแฝงอยู่ในโค้ดชั้นแอปพลิเคชัน” ก็ยอมรับได้ยาก
อย่างไรก็ยังต้อง query relation หรือคง relational view ให้เป็นปัจจุบัน หรือมีบางอย่างทำนองนั้น
ต่อให้ relation ไม่ใช่แกนหลักของ persistence model ก็ไม่เป็นไร แต่มันต้องถูก implement อยู่ที่ไหนสักแห่งใน data layer ทว่าในที่นี้ไม่เห็นพูดถึงเรื่องนั้น
Firestore ก็มีปัญหาเดียวกัน ทุกคนจัดการ relation กันด้วยวิธีใดวิธีหนึ่ง แต่สุดท้ายกลายเป็นโค้ดแอปพลิเคชันแบบสปาเกตตีที่ขยายต่อไม่ได้
ถ้าคุ้นกับ functional programming มันโดยแก่นแท้ก็เหมือน operation แบบ fold ที่พับ event stream ให้เป็นสถานะหนึ่ง
จากที่เคยทำงานกับระบบ event sourcing มาก่อน ข้อดีคือมีประวัติ event ที่จัดเก็บไว้อย่างชัดเจน แต่ความซับซ้อนก็เพิ่มขึ้นมากเช่นกัน
จะเกิดประเด็นอย่างการสร้าง read model จริง ๆ อย่างไร, จะจัดการ version ของ model อย่างไร, จะมี snapshot ของ read model หรือไม่
จากประสบการณ์ของผม ในบริบทส่วนใหญ่ที่ใช้ pattern นี้ ความซับซ้อนที่เพิ่มขึ้นไม่คุ้มค่า
สิ่งที่จำเป็นคือ command queue command event ไม่ใช่ domain event