Event Sourcing Architecture: ออกแบบระบบโดยใช้เหตุการณ์เป็นแหล่งข้อมูลหลัก
1. Event Sourcing คืออะไร?
Event Sourcing คือรูปแบบการออกแบบซอฟต์แวร์ที่ใช้เหตุการณ์ (Events) ซึ่งบันทึกการเปลี่ยนแปลงของข้อมูล เป็นแหล่งข้อมูลหลักสำหรับสร้างสถานะของระบบ แทนการจัดเก็บเฉพาะสถานะล่าสุดแล้วเขียนทับค่าเดิม
ระบบฐานข้อมูลทั่วไปมักเก็บสถานะปัจจุบัน เช่น ยอดเงินคงเหลือของบัญชี แต่ระบบ Event Sourcing จะเก็บเหตุการณ์ที่ทำให้ยอดเงินเปลี่ยนแปลง เช่น การฝากเงิน การถอนเงิน และการโอนเงิน
ตัวอย่างเช่น บัญชีธนาคารมีเหตุการณ์ดังนี้
AccountOpened +0 บาท
MoneyDeposited +5000 บาท
MoneyWithdrawn -1200 บาท
MoneyDeposited +2000 บาท
ระบบสามารถนำเหตุการณ์เหล่านี้มาประมวลผลตามลำดับ เพื่อคำนวณยอดเงินปัจจุบันได้
ยอดเงินคงเหลือ = 0 + 5,000 - 1,200 + 2,000 = 5,800 บาท
สิ่งสำคัญคือ เหตุการณ์ที่เกิดขึ้นแล้วควรได้รับการเก็บรักษาตามข้อกำหนดของระบบ โดยทั่วไปจะไม่แก้ไขเหตุการณ์เดิมเพื่อเปลี่ยนประวัติ แต่จะบันทึกเหตุการณ์ใหม่เมื่อมีการเปลี่ยนแปลงเพิ่มเติม
อย่างไรก็ตาม การจัดเก็บเหตุการณ์ไม่ได้หมายความว่าจะเก็บข้อมูลทุกอย่างได้ตลอดไป เพราะยังต้องคำนึงถึงนโยบายการเก็บรักษาข้อมูล ความเป็นส่วนตัว และข้อกำหนดทางกฎหมาย
2. Traditional CRUD vs Event Sourcing
เพื่อให้เข้าใจแนวคิดนี้ชัดเจน ควรเปรียบเทียบกับการออกแบบระบบแบบ CRUD ที่ใช้กันทั่วไป
สมมติว่าระบบต้องจัดเก็บยอดเงินในบัญชี
2.1 Traditional CRUD
ฐานข้อมูลอาจเก็บสถานะดังนี้
accounts
--------------------------------
account_id | balance
--------------------------------
ACC-001 | 5800
เมื่อมีการฝากหรือถอนเงิน ระบบจะอัปเดตยอดเงินในแถวเดิม
ข้อดีคือเข้าใจง่ายและเหมาะกับระบบธุรกิจทั่วไปจำนวนมาก แต่หากไม่มี Transaction History แยกต่างหาก การตรวจสอบว่าการเปลี่ยนแปลงเกิดขึ้นอย่างไรอาจทำได้ยาก
2.2 Event Sourcing
ระบบจะเก็บเหตุการณ์ที่เกี่ยวข้องกับบัญชี
account_events
------------------------------------------------
event_id | account_id | event_type | amount
------------------------------------------------
EVT-001 | ACC-001 | Opened | 0
EVT-002 | ACC-001 | Deposited | 5000
EVT-003 | ACC-001 | Withdrawn | 1200
EVT-004 | ACC-001 | Deposited | 2000
ระบบสามารถนำเหตุการณ์มาคำนวณสถานะปัจจุบัน และสร้างมุมมองข้อมูลอื่นได้
|
ประเด็น |
Traditional CRUD |
Event Sourcing |
|
ข้อมูลหลัก |
สถานะปัจจุบัน |
ประวัติเหตุการณ์ |
|
การเปลี่ยนแปลง |
อัปเดตข้อมูลตามโมเดล |
เพิ่มเหตุการณ์ใหม่ |
|
ประวัติย้อนหลัง |
ต้องออกแบบเก็บแยก |
มีอยู่ใน Event History |
|
การสร้างสถานะใหม่ |
อ่านข้อมูลปัจจุบัน |
Replay เหตุการณ์ |
|
ความซับซ้อน |
โดยทั่วไปต่ำกว่า |
สูงกว่า |
|
การตรวจสอบย้อนหลัง |
ขึ้นอยู่กับระบบ Audit |
ติดตามเหตุการณ์ได้โดยตรง |
Event Sourcing ไม่ได้ดีกว่า CRUD ในทุกสถานการณ์ แต่เหมาะกับระบบที่ประวัติการเปลี่ยนแปลงมีความสำคัญต่อการทำงานของธุรกิจ
3. องค์ประกอบของ Event Sourcing Architecture
ระบบ Event Sourcing โดยทั่วไปประกอบด้วยองค์ประกอบต่อไปนี้
3.1 Command
Command คือคำขอให้ระบบดำเนินการบางอย่าง เช่น
- OpenAccount
- DepositMoney
- WithdrawMoney
- CancelOrder
Command แสดงเจตนาที่ต้องการให้เกิดขึ้น ไม่ได้หมายความว่าการดำเนินการนั้นสำเร็จแล้ว
3.2 Command Handler
Command Handler ทำหน้าที่รับ Command ตรวจสอบเงื่อนไข และตัดสินใจว่าระบบสามารถดำเนินการต่อได้หรือไม่
ตัวอย่างเช่น เมื่อได้รับ WithdrawMoney ระบบต้องตรวจสอบว่ายอดเงินเพียงพอหรือไม่ ก่อนสร้างเหตุการณ์ MoneyWithdrawn
3.3 Event Store
Event Store คือพื้นที่จัดเก็บเหตุการณ์ที่ระบบใช้เป็นแหล่งข้อมูลหลัก
ตัวอย่าง Event:
{
"event_id": "evt-001",
"aggregate_id": "ACC-001",
"event_type": "MoneyDeposited",
"amount": 5000,
"currency": "THB",
"version": 1
}
ระบบจริงอาจเก็บ Metadata เพิ่มเติม เช่น เวลาเกิดเหตุการณ์ ผู้สร้างเหตุการณ์ และ Schema Version
3.4 Aggregate
Aggregate เป็นแนวคิดจาก Domain-Driven Design ที่ใช้กำหนดขอบเขตของสถานะและกฎทางธุรกิจที่ต้องรักษาความถูกต้องร่วมกัน
ตัวอย่างเช่น BankAccount อาจเป็น Aggregate ที่ควบคุมยอดเงินและตรวจสอบเงื่อนไขการถอนเงิน
3.5 Projection
Projection คือการนำเหตุการณ์มาประมวลผลเพื่อสร้างมุมมองข้อมูลที่เหมาะกับการอ่านหรือการใช้งานเฉพาะด้าน
ตัวอย่างเช่น นำเหตุการณ์ฝากและถอนเงินมาสร้างตารางยอดเงินคงเหลือสำหรับแสดงผลในแอปพลิเคชัน
3.6 Event Handler
Event Handler ทำหน้าที่ตอบสนองต่อเหตุการณ์ เช่น ส่งการแจ้งเตือน อัปเดตมุมมองข้อมูล หรือเริ่ม Workflow ถัดไป
Event Handler อาจทำงานอยู่ภายในบริการเดียวกันหรือเป็นส่วนหนึ่งของบริการอื่นในระบบกระจายศูนย์
4. ลำดับการทำงานของ Event Sourcing
ตัวอย่างระบบบัญชีธนาคาร
User
|
v
Command
|
v
Command Handler
|
v
Validate Business Rules
|
v
Generate Domain Event
|
v
Event Store
|
v
Update Projection
|
v
Application Query
|
v
Display Current Balance
อธิบายทีละขั้นตอน
- ผู้ใช้ส่งคำสั่งฝากเงิน
- Command Handler ตรวจสอบคำสั่งและเงื่อนไขทางธุรกิจ
- ระบบสร้างเหตุการณ์ MoneyDeposited เมื่อคำสั่งผ่านการตรวจสอบ
- บันทึกเหตุการณ์ลง Event Store
- Projection Processor นำเหตุการณ์ไปปรับปรุงมุมมองยอดเงิน
- แอปพลิเคชันอ่านยอดเงินจากมุมมองข้อมูลเพื่อแสดงผล
ในระบบจริงต้องจัดการกรณีที่การบันทึก Event Store ล้มเหลว รวมถึงกรณีที่บันทึกสำเร็จแล้วแต่ Projection ยังไม่อัปเดต
นอกจากนี้ การตรวจสอบและบันทึกเหตุการณ์ต้องป้องกันไม่ให้คำสั่งที่แข่งขันกันทำให้สถานะผิดพลาด เช่น การถอนเงินสองรายการพร้อมกันจนเกินยอดเงินที่มีอยู่
5. Event Replay: สร้างสถานะจากเหตุการณ์ย้อนหลัง
หนึ่งในความสามารถสำคัญของ Event Sourcing คือการนำเหตุการณ์เก่ากลับมาประมวลผล เพื่อสร้างสถานะของระบบขึ้นใหม่
ตัวอย่าง:
Event 1: AccountOpened
Event 2: MoneyDeposited(5000)
Event 3: MoneyWithdrawn(1200)
Event 4: MoneyDeposited(2000)
ระบบนำเหตุการณ์ตามลำดับมาประมวลผล
Initial Balance = 0
After Event 1 = 0
After Event 2 = 5000
After Event 3 = 3800
After Event 4 = 5800
Event Replay มีประโยชน์เมื่อ
- ต้องสร้าง Projection ใหม่
- ต้องตรวจสอบความผิดปกติของข้อมูล
- ต้องสร้างรายงานจากประวัติธุรกรรม
- ต้องทดสอบกฎทางธุรกิจเวอร์ชันใหม่
- ต้องกู้คืนมุมมองข้อมูลหลังเกิดความเสียหาย
อย่างไรก็ตาม การ Replay ไม่ได้หมายความว่าควรเรียกใช้ผลข้างเคียงภายนอกทั้งหมดซ้ำ เช่น การเรียกเก็บเงินหรือส่งอีเมลจริง ระบบควรแยกการคำนวณสถานะออกจากการดำเนินการภายนอกที่ไม่ควรเกิดซ้ำ
6. Snapshot: ลดต้นทุนการสร้างสถานะใหม่
เมื่อระบบมีเหตุการณ์จำนวนมาก การอ่านเหตุการณ์ตั้งแต่ต้นทุกครั้งอาจใช้เวลานาน
Snapshot จึงถูกนำมาใช้เพื่อบันทึกสถานะ ณ จุดหนึ่ง
ตัวอย่าง:
Events 1 - 1000
|
v
Create Snapshot
Balance = 250000
Events 1001 - 1050
|
v
Replay from Snapshot
|
v
Current Balance
แทนที่จะอ่านเหตุการณ์ทั้งหมด 1,050 รายการ ระบบสามารถโหลด Snapshot แล้วประมวลผลเฉพาะเหตุการณ์หลัง Snapshot
Snapshot เป็นข้อมูลที่สร้างขึ้นจาก Event History ไม่ใช่สิ่งทดแทน Event Store ในสถาปัตยกรรม Event Sourcing ทั่วไป
หาก Snapshot สูญหาย ระบบควรมีวิธีสร้างใหม่จากเหตุการณ์ที่เก็บไว้
7. CQRS: ทำงานร่วมกับ Event Sourcing อย่างไร?
CQRS ย่อมาจาก Command Query Responsibility Segregation เป็นแนวทางที่แยกโมเดลสำหรับการเปลี่ยนแปลงข้อมูลออกจากโมเดลสำหรับการอ่านข้อมูล
CQRS สามารถใช้ร่วมกับ Event Sourcing ได้ แต่ทั้งสองแนวคิดไม่ใช่สิ่งเดียวกัน และไม่จำเป็นต้องนำมาใช้พร้อมกันเสมอไป
ตัวอย่างสถาปัตยกรรม:
Commands
|
v
Command Model
|
v
Event Store
|
v
Event Processor
|
v
Read Model
|
v
Queries
ข้อดีคือสามารถออกแบบ Read Model ให้เหมาะกับการค้นหาและรายงานได้ โดยไม่ต้องใช้โครงสร้างเดียวกับโมเดลธุรกิจที่รับผิดชอบการเปลี่ยนแปลงข้อมูล
ข้อแลกเปลี่ยนคือ Read Model อาจอัปเดตช้ากว่า Event Store เล็กน้อย ทำให้ระบบมี Eventual Consistency
8. Event Sourcing กับ Apache Kafka
Apache Kafka สามารถใช้เป็นส่วนหนึ่งของสถาปัตยกรรม Event Sourcing ได้ แต่ต้องแยกบทบาทให้ชัดเจน
Kafka เป็น Distributed Event Streaming Platform ส่วน Event Store เป็นองค์ประกอบเชิงสถาปัตยกรรมที่ทำหน้าที่จัดเก็บเหตุการณ์หลักของระบบ
Kafka สามารถจัดเก็บ Event Streams และรองรับการอ่านย้อนหลังตาม Retention Policy ได้ แต่การใช้ Kafka เพียงอย่างเดียวไม่ได้รับประกันว่าจะมีความสามารถด้าน Event Store ครบถ้วน เช่น การโหลดเหตุการณ์ตาม Aggregate ID การตรวจสอบเวอร์ชันของ Aggregate และการป้องกันการเขียนเหตุการณ์ที่ขัดแย้งกัน
ตัวอย่างการใช้งานร่วมกัน:
Application
|
v
Event Store
|
v
Event Publisher
|
v
Apache Kafka
|
+------> Analytics
|
+------> Notification
|
+------> Search Index
|
+------> Data Platform
หากระบบใช้ฐานข้อมูลเป็น Event Store และเผยแพร่เหตุการณ์ไปยัง Kafka ควรพิจารณา Transactional Outbox Pattern เพื่อป้องกันความไม่สอดคล้องระหว่างการบันทึกเหตุการณ์ในฐานข้อมูลกับการเผยแพร่ Event
9. ข้อดีของ Event Sourcing
9.1 ตรวจสอบประวัติการเปลี่ยนแปลงได้
ระบบมีประวัติเหตุการณ์ที่ช่วยอธิบายว่าสถานะปัจจุบันเกิดขึ้นได้อย่างไร
9.2 สร้างมุมมองข้อมูลใหม่ได้
สามารถ Replay เหตุการณ์เพื่อสร้าง Projection หรือรายงานรูปแบบใหม่ได้
9.3 รองรับการวิเคราะห์เชิงลึก
ประวัติเหตุการณ์ช่วยให้วิเคราะห์รูปแบบการใช้งานและลำดับการทำธุรกรรมได้ละเอียดขึ้น
9.4 แยกโมเดลการเขียนและการอ่านได้
เมื่อใช้ร่วมกับ CQRS ระบบสามารถปรับแต่งการประมวลผลคำสั่งและการค้นหาให้เหมาะกับงานแต่ละประเภทได้
9.5 ช่วยในการตรวจสอบข้อผิดพลาด
ทีมพัฒนาสามารถใช้ประวัติเหตุการณ์เพื่อทำความเข้าใจสถานะและลำดับการเปลี่ยนแปลงของระบบ
ทั้งนี้ หากต้องการ Audit Trail ที่มีคุณสมบัติป้องกันการแก้ไขข้อมูลอย่างเข้มงวด ต้องออกแบบสิทธิ์การเข้าถึง การเก็บรักษา และกลไกตรวจสอบเพิ่มเติม เพราะ Event Sourcing เพียงอย่างเดียวไม่ได้รับประกันว่าไม่มีผู้ใดแก้ไขหรือลบข้อมูลได้
10. ข้อจำกัดและความท้าทาย
Event Sourcing เพิ่มความสามารถในการติดตามประวัติ แต่ก็เพิ่มความซับซ้อนของระบบด้วย
Event Versioning: เมื่อโครงสร้าง Event เปลี่ยนแปลง ต้องมีแนวทางรองรับข้อมูลเก่า
Concurrency: ต้องป้องกันการบันทึกเหตุการณ์ที่ขัดแย้งกัน โดยเฉพาะเมื่อหลายคำสั่งแก้ไข Aggregate เดียวกัน
Eventual Consistency: Projection อาจไม่สะท้อนสถานะล่าสุดทันที
Replay Cost: การสร้างสถานะใหม่จากเหตุการณ์จำนวนมากอาจใช้ทรัพยากรสูง
Side Effects: การประมวลผลย้อนหลังต้องไม่ทำให้ธุรกรรมภายนอกเกิดซ้ำโดยไม่ตั้งใจ
Data Privacy: ข้อมูลส่วนบุคคลที่ถูกเก็บไว้ใน Event อาจต้องมีนโยบายลบ ปกปิด หรือจำกัดการเข้าถึงตามข้อกำหนดที่เกี่ยวข้อง
ด้วยเหตุนี้ Event Sourcing จึงเหมาะกับระบบที่ประโยชน์จากประวัติเหตุการณ์มีมูลค่ามากกว่าต้นทุนและความซับซ้อนที่เพิ่มขึ้น
11. ตัวอย่างการนำไปใช้ในระบบธุรกิจ
|
ระบบ |
เหตุการณ์ตัวอย่าง |
ประโยชน์ |
|
E-Commerce |
OrderCreated, OrderCancelled |
ตรวจสอบวงจรชีวิตของคำสั่งซื้อ |
|
Banking |
MoneyDeposited, MoneyWithdrawn |
ติดตามประวัติธุรกรรม |
|
Inventory |
StockReceived, StockReserved |
ตรวจสอบการเปลี่ยนแปลงสต็อก |
|
Booking |
ReservationCreated, ReservationCancelled |
ตรวจสอบสถานะการจอง |
|
Logistics |
ShipmentCreated, ShipmentDelivered |
ติดตามการจัดส่ง |
|
AI Agent |
TaskStarted, ToolCalled, TaskCompleted |
ติดตาม Workflow ของ Agent |
สำหรับ AI Agent ควรแยก Domain Event ที่ใช้ขับเคลื่อนสถานะของระบบออกจาก Observability Log ที่บันทึกเพื่อการตรวจสอบและวิเคราะห์ เพราะข้อมูลสองประเภทนี้มีวัตถุประสงค์และข้อกำหนดในการเก็บรักษาที่แตกต่างกัน
12. แนวทางการพัฒนา Event Sourcing ให้พร้อมใช้งานจริง
ก่อนนำไปใช้ใน Production ควรพิจารณาประเด็นต่อไปนี้
- กำหนด Aggregate และขอบเขตความถูกต้องของธุรกิจ
- ออกแบบ Event Schema ให้สื่อความหมายและรองรับการเปลี่ยนแปลง
- ใช้ Optimistic Concurrency Control หรือกลไกที่เหมาะสม
- ออกแบบ Projection ให้สามารถสร้างใหม่ได้
- กำหนดนโยบาย Snapshot ตามปริมาณข้อมูล
- ป้องกันการประมวลผลซ้ำและการเกิด Side Effects
- วางแผนการสำรองข้อมูลและกู้คืนระบบ
- กำหนดนโยบายด้าน Security และ Data Privacy
- ทดสอบการ Replay และการเปลี่ยน Schema
- ติดตามความล่าช้าของ Event Processing และ Projection
ไม่จำเป็นต้องใช้ Event Sourcing กับทุกโมดูลของระบบ อาจใช้เฉพาะส่วนที่ต้องการประวัติธุรกรรมที่ละเอียด เช่น การชำระเงิน การจัดการคำสั่งซื้อ หรือการควบคุมสถานะ Workflow
13. บทสรุป
Event Sourcing เปลี่ยนแนวคิดการจัดเก็บข้อมูลจากการมองเฉพาะสถานะปัจจุบัน ไปสู่การมองเหตุการณ์ที่ทำให้สถานะนั้นเกิดขึ้น
เมื่อทำงานร่วมกับ CQRS, Projection, Snapshot และ Distributed Event Streaming ระบบจะสามารถติดตามประวัติ สร้างมุมมองข้อมูลใหม่ และเชื่อมต่อกับบริการอื่นได้อย่างยืดหยุ่น
อย่างไรก็ตาม การออกแบบ Event Sourcing ต้องให้ความสำคัญกับ Concurrency, Event Versioning, Replay, Consistency และ Data Privacy ตั้งแต่ต้น
หลักการสำคัญ: ใช้ Event Sourcing เมื่อประวัติการเปลี่ยนแปลงเป็นส่วนสำคัญของโมเดลธุรกิจ ไม่ใช่เพียงเพราะต้องการใช้เทคโนโลยีใหม่
ความคิดเห็น
แสดงความคิดเห็น