บทที่ 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 แสดงได้หลายระดับ:

  1. methods/DTOs แยก read/write ใน process เดียว
  2. models/repositories แยก แต่ shared database
  3. read projection แยก store และ async update
  4. 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 สำหรับการเปลี่ยนเงิน

อ่านเพิ่มเติม