บทที่ 25 · Part 5 — From Model to Running System
Commands, Queries and Read Models
แยก intent, decision และ display needs โดยไม่กระโดดไป CQRS infrastructure หรือใช้ read model ตัดสินเงิน
Commands, Queries and Read Models
Portal ต้องแสดง transfer history จากหลาย contexts แต่การให้มันโหลด Aggregates หรือ join authoritative tables ทำให้ channel กลายเป็น domain owner คำสั่งเปลี่ยน state, query ขอข้อมูล และ read model เพื่อ display มี consistency/authority ต่างกัน การแยกนี้ไม่จำเป็นต้องเริ่มด้วย CQRS infrastructure
จบบทนี้คุณจะ
แยก Command, Query และ Event ตาม semantics ออกแบบ read model จาก user need ระบุ freshness/ partial state และห้ามใช้ display projection ตัดสินเงินโดยเงียบ ประเมิน CQRS หลายระดับโดยรวม complexity/operations cost
Command คือ Intent
Command มี actor, target, identity และอาจถูก reject:
Confirm Transfer
Cancel Transfer Intent
Approve Reconciliation Correction
ชื่อ imperative และ handler context owner ตัดสิน invariant ไม่ตั้งชื่อ UpdateStatus
Query ไม่เปลี่ยน Business State
Query ขอข้อมูลตาม consumer need:
Get Transfer Details
List Reconciliation Exceptions
Show Customer Transfer Timeline
Query อาจบันทึก technical telemetry แต่ไม่ซ่อน business side effect
Read Model ตามหน้าจอ/งาน
Portal projection อาจรวม:
Transfer label
Customer-facing status
Displayed amount/currency
Last updated time
Pending explanation
มันไม่ต้องเหมือน write/domain model และสร้างใหม่ได้จาก authoritative sources ตาม contract
Display Path กับ Decision Path
Displayed Balance หรือ Transfer Status ยอม stale ได้ตาม freshness contract แต่ Reserve Funds, Approve Refund หรือ Post Correction ต้องกลับไป owning context ตรวจ authoritative state/invariant ห้าม reuse projection เพราะอยู่ใกล้ UI
Freshness Contract
read model ระบุ:
- source contexts
- last updated / cutoff
- expected lag
- missing/partial behavior
- rebuild/replay process
- consumer decisions allowed/forbidden
- owner เมื่อ projection ผิด
UI ควรแสดง “กำลังอัปเดต” แทนสร้าง certainty ปลอม
เส้นประเป็นข้อห้ามเชิง authority: ผู้ใช้อาจเริ่ม Command จากหน้าจอได้ แต่ owning context ต้องตรวจ state ใหม่ หน้าจอหรือ cache ไม่มีสิทธิ์อนุมัติด้วยค่าที่แสดงอยู่
CQRS เป็น Spectrum
Azure CQRS Pattern แสดงได้หลายระดับ:
- methods/DTOs แยก read/write ใน process เดียว
- models/repositories แยก แต่ shared database
- read projection แยก store และ async update
- separate deployables ตาม scale/ownership
เลือกขั้นต่ำที่แก้ force Simple read DTO ไม่ต้อง message broker/database ใหม่
Query Across Contexts
ทางเลือก:
- synchronous composition จาก public APIs
- materialized projection จาก integration events
- BFF/cache ที่ rebuild ได้
- analytics/data product สำหรับ non-operational use
ห้าม direct write และระวัง cross-context DB join ที่ทำ consumer พึ่ง schemas ภายใน
Event ไม่ใช่ Command
TransferConfirmed เป็น fact ไม่มี consumer เป้าหมายเฉพาะ PostLedgerEntries เป็น intent ที่อาจ
reject หากใช้ event เพื่อสั่งงานโดยซ่อนผู้รับ ownership/failure จะกำกวม ตั้งชื่อ semantics ตรง
แบบฝึกปฏิบัติ: Transfer Details Screen
ออกแบบ:
User questions:
Source contexts:
Projection fields and meanings:
Freshness / partial data:
Allowed display statements:
Forbidden financial decisions:
Update mechanism alternatives:
Rebuild and support owner:
เปรียบเทียบ sync composition กับ async projection สำหรับ small business และ corporate
รายการตรวจสอบ
- Command เป็น intent ที่ context อาจ reject
- Query ไม่มี hidden business mutation
- Read model ตาม consumer question
- Freshness/partial/rebuild contract ชัด
- Display projection ไม่ใช้ financial decision
- CQRS เริ่มขั้นต่ำ
- Cross-context read ไม่ละเมิด data ownership
สรุปบทนี้
Commands, Queries และ Read Models แยก intention, decision authority และ presentation needs CQRS มีหลายระดับและควรเลือกจาก force จุดสำคัญใน Fintech คือ projection ที่ดีต่อ display ไม่ได้กลายเป็น authoritative state สำหรับการเปลี่ยนเงิน
อ่านเพิ่มเติม
- CQRS Pattern — Azure
- DDD Reference — domain/application concepts
- Monzo Balance Case — semantics ของ balance