บทที่ 3 · Part 1 — Foundations and Economics
Models as Working Theories
เข้าใจ model ในฐานะสมมติฐานที่เลือกเพื่อแก้ปัญหาและต้องเปลี่ยนตาม evidence ไม่ใช่ภาพแทนโลกทั้งหมด
Models as Working Theories
ทีม Payment เคยวาด model ว่า Transfer มีสถานะ PENDING, SUCCESS, FAILED แล้วใช้ชุดเดียว
ทั้งหน้าจอลูกค้า การส่งคำสั่งไป provider และการลงบัญชี เมื่อ provider timeout ระบบจึงบังคับให้เลือก
FAILED ทั้งที่เงินจริงอาจเคลื่อนไปแล้ว ปัญหาไม่ใช่ enum มีค่าน้อย แต่ model เลือกอธิบายโลก
ผิดมุมและไม่มีที่วางสิ่งที่ “ยังไม่ทราบ”
จบบทนี้คุณจะ
อธิบาย model ในฐานะ working theory ที่เลือก concept เพื่อจุดประสงค์หนึ่ง แยก model จาก database schema และภาพเหมือนของโลก สร้าง alternative models จาก scenario เดียว และกำหนด evidence ที่จะทำให้ยอมแก้ model
[!IMPORTANT] Model มี Purpose และ Boundary DDD Reference วาง Model-Driven Design ไว้บนความสัมพันธ์ระหว่าง model, language และ implementation Model ที่ดีไม่จำเป็นต้องเก็บ ข้อเท็จจริงทุกอย่าง แต่ต้องช่วยให้ทีมอธิบายและตัดสินปัญหาที่รับผิดชอบได้
Model ไม่ใช่สำเนาของโลก
แผนที่รถไฟเลือกสถานีและเส้นทาง แต่ไม่วาดต้นไม้ทุกต้น เช่นเดียวกัน model ของ Transfer อาจเลือก identity, lifecycle, amount, destination, authorization และ execution outcome โดยไม่เก็บ ทุก field จาก provider การตัดสิ่งที่ไม่เกี่ยวข้องออกเรียกว่า abstraction ไม่ใช่ข้อมูลสูญหาย
ถาม model ทุกชุดด้วย 4 คำถาม:
- ใช้ตัดสินหรืออธิบายอะไร
- ใครใช้ภาษาและตรวจความถูกต้องของมัน
- ใช้ได้ภายใน context และช่วงเวลาใด
- scenario หรือ evidence ใดทำให้มันผิด
ถ้าตอบเพียง “ใช้แทน table transfers” แสดงว่ายังไม่มี domain purpose ชัด Database schema
อาจรวม concerns เพื่อ operational convenience ขณะที่ model แยกเพื่อรักษาความหมาย หรือกลับกัน
model หนึ่งอาจประกอบข้อมูลจากหลาย tables โดยไม่ให้ schema เป็นผู้กำหนด language
หลาย Model สำหรับคำเดียวกัน
คำว่า Transfer อาจมี model ต่างกันโดยตั้งใจ:
| Context | Model สนใจ | คำถามหลัก |
|---|---|---|
| Customer Intent | ผู้ขอ, beneficiary, amount, expiry | ลูกค้ายังต้องการส่งเงินอยู่หรือไม่ |
| Risk Decision | subject, evidence, policy version, decision | อนุญาตภายใต้ policy ใด |
| Payment Execution | provider reference, attempt, outcome | คำสั่งภายนอกอยู่สถานะใด |
| Ledger | entries, accounts, posting reference | financial fact ใดถูกบันทึกแล้ว |
| Customer Display | label, progress, estimated completion | ควรอธิบายอะไรให้ลูกค้าเห็น |
การมี model ต่างกันไม่ใช่ duplication โดยอัตโนมัติ แต่การใช้ชื่อเหมือนกันโดยไม่ระบุ context
สร้างความเข้าใจผิด EXECUTION_ACCEPTED ไม่ได้แปลว่า FUNDS_POSTED และคำว่า “สำเร็จ”
ต้องถูกท้าทายจนบอกได้ว่าสำเร็จสำหรับใคร
Model เป็นสมมติฐานที่ทดสอบได้
เริ่มด้วยประโยค:
เราเชื่อว่า Transfer Intent ควรแยกจาก Execution Attempt เพราะลูกค้าขอ 1 ครั้งได้แต่ระบบ อาจลองหลาย provider และแต่ละ attempt มี uncertain outcome ของตนเอง
จากนั้นหา counterexamples:
- ลูกค้าแก้ beneficiary หลัง attempt แรกถูกส่งแล้วได้หรือไม่
- provider timeout แล้ว callback สำเร็จมาช้าจะผูกกับ intent ใด
- retry เป็น attempt ใหม่หรือการส่ง request เดิมด้วย idempotency key เดิม
- transfer หมดอายุใน UI แต่ execution ยัง settle ภายหลังได้หรือไม่
ถ้า model ตอบไม่ได้ อย่ารีบเพิ่ม field ให้ทุก object ลองเปลี่ยน relationship, boundary หรือชื่อ
ก่อน Model ที่ดีทำให้ความขัดแย้งเป็นคำถามเฉพาะ ไม่ใช่ซ่อนด้วย metadata map[string]any
Whirlpool ของการเรียนรู้
Eric Evans อธิบาย Whirlpool เป็นวงรอบ ระหว่าง scenario, model และการทดลองใน code ไม่ใช่ phase gate เต็มรูปแบบ รอบหนึ่งอาจเป็น:
วงรอบนี้ไม่มีจุด “model เสร็จแล้ว” การใช้งานจริงสร้าง evidence รอบใหม่เสมอ และลูกศรกลับไม่ได้ หมายถึง rewrite ทุกครั้ง แต่อาจแก้เพียงชื่อ, example, invariant หรือ boundary hypothesis
การเขียน code มีบทบาทเป็น modelling probe ถ้า method ต้องรับ flags 7 ตัว หรือ Aggregate ต้องโหลด object graph ใหญ่เพื่อคำสั่งเล็ก อาจเป็น evidence ว่า concept/boundary ยังไม่ดี ไม่ใช่สัญญาณให้เพิ่ม design pattern โดยอัตโนมัติ
Deep Model กับ Useful Model
Deep Model ทำให้ความเข้าใจสำคัญถูกแสดงด้วย concept ที่กระชับ เช่นแยก Available Funds
จาก Displayed Balance ช่วยตัดสินว่า read model ใดใช้อนุมัติเงินได้ แต่ “ deep” ไม่ได้แปลว่า
class เยอะหรือครอบทุก exception ตั้งแต่วันแรก
ให้ model มี explanatory power:
- คำสำคัญลดประโยคอธิบายยาวได้หรือไม่
- invalid state ถูกตั้งชื่อหรือป้องกันได้หรือไม่
- business expert ใช้ model อธิบาย counterexample ได้หรือไม่
- change ใหม่ตกใน concept ที่มีอยู่ หรือบังคับให้แก้ทั่วระบบ
- model บอกสิ่งที่จงใจไม่รับผิดชอบหรือไม่
Model เรียบง่ายอาจเป็นคำตอบที่ลึกถ้ามันตัด distinction ที่ไม่จำเป็นออกได้ถูก
Financial Model ต้องแยก Fact จาก Projection
Ledger entry, provider report, calculated balance และ UI projection อาจเกี่ยวข้องกันแต่มี authority ต่างกัน การแก้ model เพื่อให้ยอด “ดูตรง” ห้าม rewrite financial fact โดยไม่มี accountable owner, correction policy และ audit requirement ที่ยืนยันแล้ว
แบบฝึกปฏิบัติ: Three Models of Transfer
สร้าง model alternatives 3 แบบจาก scenario “ลูกค้าส่งเงินแล้ว provider timeout”:
Model purpose:
Key concepts and meanings:
State transitions:
Facts owned here:
Facts accepted from elsewhere:
Question this model answers:
Question deliberately outside it:
Counterexample that breaks it:
Evidence needed next:
แบบที่ 1 ใช้ Transfer object เดียว แบบที่ 2 แยก Intent/Attempt แบบที่ 3 แยก Execution กับ Ledger Posting เปรียบเทียบว่าทางเลือกใดทำ unknown outcome และ retry semantics ชัดที่สุด โดยยังไม่ตัดสิน package หรือ microservice
รายการตรวจสอบ
- Model มี purpose และ context ไม่ใช่สำเนา table
- Concept ถูกเลือกเพื่ออธิบาย decision ไม่ใช่เก็บทุก fact
- คำเดียวหลาย model ถูกตั้งชื่อและแยกความหมาย
- มี counterexample ที่ทำให้ model เปลี่ยนได้
- Code/test ถูกใช้เป็น feedback ต่อ model
- Fact, projection และ display state ไม่ถูกปนกัน
- Boundary หรือ concept เปลี่ยนได้เมื่อ evidence เปลี่ยน
สรุปบทนี้
Model คือ working theory ที่มีประโยชน์ในบริบทหนึ่ง ไม่ใช่ความจริงถาวร DDD ทำให้ทีมเดินวน ระหว่าง scenario, language, model และ implementation เพื่อค้นหา abstraction ที่อธิบายปัญหาได้ดีขึ้น ความสามารถในการยอมทิ้ง model ที่ evidence ไม่รองรับสำคัญพอ ๆ กับการสร้าง model แรก
อ่านเพิ่มเติม
- DDD Reference — Model-Driven Design และ Supple Design
- The Whirlpool — วงรอบ modelling จาก Eric Evans
- Domain-Driven Design — ภาพรวม model และ Strategic Design ของ Martin Fowler