ตอนที่ 4 ของซีรีส์ Domain-Driven Design ฉบับระบบธุรกิจจริง
หลังจากตอนที่ 3 เราได้ภาษาเดียวกันระหว่างโค้ดกับธุรกิจแล้ว
ตอนนี้ถึงเวลาลงมือ สร้าง Domain Model จริง ๆ
คำถามแรกที่สำคัญมากคือ:
สิ่งนี้ในระบบ ควรเป็น Entity หรือ Value Object ?
ถ้าตัดสินใจผิดตั้งแต่ตรงนี้ Domain จะเริ่มบิดทันที
ความเข้าใจผิดที่พบบ่อย
นักพัฒนาระบบธุรกิจจำนวนมากมักคิดว่า:
“อะไรก็ตามที่มาจาก database = Entity”
ผลลัพธ์คือ:
- ทุกอย่างมี id
- ทุกอย่างแก้ค่าได้หมด
- Domain กลายเป็นถุงข้อมูล (Anemic Model)
DDD แยกสิ่งเหล่านี้ออกชัดเจนกว่า
Entity คืออะไร
Entity คือสิ่งที่:
- มีตัวตนเฉพาะ (identity)
- เปลี่ยนค่าได้ แต่ยังเป็น “สิ่งเดิม”
- ธุรกิจสนใจว่า เป็นใคร มากกว่า มีค่าอะไร
ในระบบพนักงาน ตัวอย่าง Entity ได้แก่:
- Staff
- ShiftRequest
- Department
แม้ข้อมูลจะเปลี่ยน แต่ identity ยังเดิม
ตัวอย่าง Entity: Staff
class Staff {
final StaffId id;
String name;
StaffStatus status;
void resign() {
if (status == StaffStatus.resigned) {
throw Exception('Staff already resigned');
}
status = StaffStatus.resigned;
}
}
ประเด็นสำคัญ:
- id คือหัวใจของ entity
- behavior อยู่กับ entity
- entity ปกป้องกฎของตัวเอง
Value Object คืออะไร
Value Object คือสิ่งที่:
- ไม่มี identity
- วัดค่าด้วย “ความเท่ากันของข้อมูล”
- มักเป็น immutable
ในระบบพนักงาน ตัวอย่าง Value Object ได้แก่:
- EmailAddress
- PhoneNumber
- WorkingPeriod
- ShiftTime
ถ้าค่าเหมือนกัน = ถือว่าเหมือนกัน
ตัวอย่าง Value Object: WorkingPeriod
class WorkingPeriod {
final DateTime start;
final DateTime end;
WorkingPeriod(this.start, this.end) {
if (!end.isAfter(start)) {
throw Exception('End must be after start');
}
}
bool overlaps(WorkingPeriod other) {
return start.isBefore(other.end) && end.isAfter(other.start);
}
}
จุดสังเกต:
- ไม่มี id
- สร้างแล้วแก้ไม่ได้
- กฎอยู่ในตัวมันเอง
ทำไม Value Object ถึงสำคัญกับระบบธุรกิจ
ถ้าคุณไม่ใช้ Value Object:
- validation จะกระจัดกระจาย
- logic ซ้ำ ๆ เต็มระบบ
- test ยาก
Value Object ทำให้:
- กฎถูกเขียนครั้งเดียว
- intent ชัดเจน
- domain อ่านง่ายขึ้นมาก
Entity + Value Object ทำงานร่วมกัน
Entity ไม่ควรเก็บ primitive ดิบ ๆ เต็มไปหมด แต่ให้ใช้ชนิดข้อมูลที่เป็น value object มาทำงานร่วมกัน
แทนที่จะเขียน:
String email;
DateTime shiftStart;
DateTime shiftEnd;
ให้เขียน:
EmailAddress email;
WorkingPeriod shiftPeriod;
ตอนนี้ domain:
- ปลอดภัยขึ้น เพราะ ค่า email หรือ shiftPeriod จะถูกตรวจสอบความถูกต้องของข้อมูลจาก value object ซึ่งก็คือ class EmailAddress และ WorkingPeriod เป็นต้น
- อ่านเหมือนภาษาธุรกิจ
- ลด bug เชิงกฎ
Checklist ตัดสินใจ: Entity หรือ Value Object
ถามตัวเองทีละข้อ:
- ธุรกิจสนใจ identity ของมันไหม?
- ถ้าข้อมูลเหมือนกัน ถือว่าเป็นอันเดียวกันได้ไหม?
- มันควรถูกแก้ไข หรือควรถูกแทนที่?
ถ้า:
- สนใจ identity → Entity
- สนใจค่า → Value Object
สรุปตอนที่ 4
- Entity = ตัวตน + behavior
- Value Object = ค่า + กฎ + immutability
- ใช้ให้ถูกตั้งแต่ต้น = domain แข็งแรง
ถ้าทุกอย่างในระบบเป็น Entity
แปลว่าคุณยังไม่ได้ออกแบบ Domain จริง ๆ
ตอนต่อไป
ตอนที่ 5: Aggregate & Aggregate Root – กำแพงป้องกัน Domain ไม่ให้เละ
เราจะเริ่มควบคุมขอบเขตของการแก้ไข และพูดถึง invariants ของระบบพนักงาน

