เมื่อไหร่ไม่ควรใช้ DDD
สิ่งที่แยกผู้ใช้ DDD ที่เก่งออกจากคนที่ “ทำตามสูตร” คือวิจารณญาณว่า “เมื่อไหร่ไม่ควรใช้” DDD มีต้นทุน และไม่ใช่คำตอบสำหรับทุกปัญหา
เมื่อไหร่ DDD คุ้มค่า
หัวข้อที่มีชื่อว่า “เมื่อไหร่ DDD คุ้มค่า”- domain ซับซ้อน มีกฎธุรกิจซับซ้อนและเปลี่ยนแปลงบ่อย (e-commerce การเงิน ERP โลจิสติกส์ ประกันภัย)
- ต้องการความร่วมมือใกล้ชิดระหว่างฝ่ายธุรกิจกับฝ่ายเทคนิค
- ส่วนนั้นเป็น Core DomainCore Domainsubdomain ที่สำคัญและสร้างความได้เปรียบในการแข่งขันมากที่สุด เป็นเหตุผลที่เราต้องสร้างซอฟต์แวร์เองแทนการซื้อ ควรทุ่มทีมเก่งที่สุดลงตรงนี้Strategic Design ที่สร้างความได้เปรียบในการแข่งขัน
เมื่อไหร่ไม่ควรใช้ DDD เต็มรูปแบบ
หัวข้อที่มีชื่อว่า “เมื่อไหร่ไม่ควรใช้ DDD เต็มรูปแบบ”ถ้าเป็น app CRUD ง่ายๆ (หน้า admin, รายงานข้อมูล, to-do list, ระบบจัดการรายชื่อ) การฝืนใช้ AggregateAggregateกลุ่มของ Entity และ Value Object ที่ถูกมองเป็นหนึ่งหน่วยเดียวเพื่อรักษาความถูกต้องของข้อมูล มีขอบเขตชัดเจน และเป็นหน่วยของ transactionTactical Design, Domain EventDomain Eventสิ่งที่ “เกิดขึ้นแล้ว” ใน domain ซึ่งส่วนอื่นสนใจ แทนด้วยอ็อบเจ็กต์ที่เปลี่ยนแปลงไม่ได้ ตั้งชื่อเป็นอดีต เช่น CargoWasRouted ใช้สื่อสารข้าม AggregateTactical Design, RepositoryRepositoryตัวที่ทำให้เราเข้าถึง Aggregate ราวกับเป็น collection ในหน่วยความจำ ซ่อนรายละเอียดฐานข้อมูล มี1 repository ต่อ1 Aggregate RootTactical Design เต็มรูปแบบจะเพิ่มความซับซ้อนโดยไม่จำเป็น ทำให้พัฒนาช้าลงและแก้ feature ง่ายๆ ยากขึ้น — กลายเป็น codebase ที่ซับซ้อนเกินความต้องการจริงและต้นทุนบานปลาย
ในระบบจริงหนึ่งระบบ มักมีทั้งสองแบบปนกัน เช่น (อ้างอิงตัวอย่าง eShopOnContainers ของ Microsoft) microservice การสั่งซื้อใช้ DDD เต็มรูปแบบเพราะกฎซับซ้อน ส่วน microservice catalog ที่เป็น CRUD ล้วนใช้ Anemic Domain ModelAnemic Domain Modelanti-pattern ที่อ็อบเจ็กต์มีแต่ getter/setter ไร้พฤติกรรม ตรรกะถูกดึงไปไว้ใน service ทั้งหมด — จ่ายค่า domain model แต่ไม่ได้ประโยชน์ของมันเลยTactical Design ก็เพียงพอ — เลือกใช้เครื่องมือตามความซับซ้อนของแต่ละส่วน
Anti-patterns ที่พบบ่อย
หัวข้อที่มีชื่อว่า “Anti-patterns ที่พบบ่อย”- ภาษาไม่สอดคล้อง — คำเดียวกันหมายถึงคนละสิ่ง โดยไม่มี Bounded ContextBounded Contextขอบเขตที่ชัดเจน (มักเป็นระบบย่อยหรือทีมหนึ่ง) ที่ model หนึ่งใช้ได้และมีความหมายแน่นอน ภายในขอบเขตนี้ Ubiquitous Language สอดคล้องกัน 100%Strategic Design มากำหนดขอบเขต
- Over-engineering งานง่าย — ยกชุดเครื่องมือ tactical ทั้งหมดมาใส่ CRUD ธรรมดา
- Value ObjectValue Objectอ็อบเจ็กต์ที่สำคัญที่ “ค่า” ของมัน ไม่มีตัวตนเฉพาะ สองตัวที่ค่าเท่ากันถือว่าเท่ากัน ควรเป็น immutable (เปลี่ยนค่าไม่ได้) เช่น เงิน, วันที่, พิกัดTactical Design ที่ผิดเพี้ยน — ใส่ identity หรือทำให้แก้ค่าได้ / EntityEntityอ็อบเจ็กต์ที่มี “ตัวตน” (identity) ต่อเนื่องตามเวลา แม้ค่าภายในเปลี่ยน ก็ยังเป็นสิ่งเดิม เช่น ลูกค้า, คำสั่งซื้อ — แยกแยะด้วย id ไม่ใช่ด้วยค่าTactical Design ที่สร้างโดยไม่มี identity ชัดเจน
- Service อ้วน — ดึงตรรกะออกจาก model จนกลายเป็น Anemic Domain ModelAnemic Domain Modelanti-pattern ที่อ็อบเจ็กต์มีแต่ getter/setter ไร้พฤติกรรม ตรรกะถูกดึงไปไว้ใน service ทั้งหมด — จ่ายค่า domain model แต่ไม่ได้ประโยชน์ของมันเลยTactical Design
- AggregateAggregateกลุ่มของ Entity และ Value Object ที่ถูกมองเป็นหนึ่งหน่วยเดียวเพื่อรักษาความถูกต้องของข้อมูล มีขอบเขตชัดเจน และเป็นหน่วยของ transactionTactical Design ใหญ่เกินไป หรืออ้างอิง Aggregate อื่นโดยตรง → transaction ข้ามขอบเขต ปัญหาประสิทธิภาพและความถูกต้อง
- กระโดดไป tactical ก่อน — ลงมือสร้าง Entity/Repository ก่อนเข้าใจ subdomain และขอบเขต
- ละเลย Core — ทำ model ส่วนรอบนอกอย่างประณีต แต่ปล่อยส่วนหัวใจของธุรกิจไว้
- Big Ball of MudBig Ball of Mudระบบที่ปนเปกันยุ่งเหยิงไม่มีขอบเขตชัด ให้ลากเส้นล้อมไว้แล้วยอมรับสภาพ ไม่พยายามทำ model ละเอียดในนั้น และระวังไม่ให้ลามไป context อื่นStrategic Design — Aggregate ปนเปกันหมด ไม่มีขอบเขตชัด code ผูกกันแน่น
เริ่มจาก Strategic ก่อนเสมอ: เข้าใจ DomainDomainขอบเขตของปัญหาหรือธุรกิจที่ซอฟต์แวร์ของเราเข้าไปแก้ไข เช่น การขนส่งสินค้า การธนาคาร e-commerce — คือ “โลกของผู้ใช้” ที่โปรแกรมต้องเข้าใจStrategic Design → แบ่ง SubdomainSubdomainส่วนย่อยของ Domain ใหญ่ เช่น Domain e-commerce แตกเป็น subdomain การสั่งซื้อ การชำระเงิน คลังสินค้า ฯลฯ อยู่ใน “พื้นที่ปัญหา” (problem space)Strategic Design → หา Bounded ContextBounded Contextขอบเขตที่ชัดเจน (มักเป็นระบบย่อยหรือทีมหนึ่ง) ที่ model หนึ่งใช้ได้และมีความหมายแน่นอน ภายในขอบเขตนี้ Ubiquitous Language สอดคล้องกัน 100%Strategic Design → วาด Context MapContext Mapแผนภาพที่แสดง Bounded Context ทั้งหมดและความสัมพันธ์ระหว่างกัน ช่วยให้เห็นภาพรวมและจุดเชื่อมต่อ/แปลภาษาในระบบใหญ่Strategic Design แล้ว จึง ลงมือ tactical การกระโดดไปเขียน EntityEntityอ็อบเจ็กต์ที่มี “ตัวตน” (identity) ต่อเนื่องตามเวลา แม้ค่าภายในเปลี่ยน ก็ยังเป็นสิ่งเดิม เช่น ลูกค้า, คำสั่งซื้อ — แยกแยะด้วย id ไม่ใช่ด้วยค่าTactical Design/RepositoryRepositoryตัวที่ทำให้เราเข้าถึง Aggregate ราวกับเป็น collection ในหน่วยความจำ ซ่อนรายละเอียดฐานข้อมูล มี1 repository ต่อ1 Aggregate RootTactical Design ทันทีเป็นสาเหตุของปัญหาเชิงโครงสร้างที่แก้ยากภายหลัง
สรุปเส้นทางทั้งหมด
หัวข้อที่มีชื่อว่า “สรุปเส้นทางทั้งหมด”คุณได้เดินครบทั้ง 5 ขั้น: เข้าใจ “ทำไม” (Stage 0) → สร้าง Ubiquitous LanguageUbiquitous Languageภาษากลางที่ทุกคนในทีม (ทั้งนักพัฒนาและผู้เชี่ยวชาญธุรกิจ) ใช้ร่วมกัน อิงกับ domain model โดยตรง คำเดียวกันต้องหมายถึงสิ่งเดียวกัน และสะท้อนลงใน codeStrategic Design (Stage 1) → ออกแบบเชิงกลยุทธ์ด้วย Bounded ContextBounded Contextขอบเขตที่ชัดเจน (มักเป็นระบบย่อยหรือทีมหนึ่ง) ที่ model หนึ่งใช้ได้และมีความหมายแน่นอน ภายในขอบเขตนี้ Ubiquitous Language สอดคล้องกัน 100%Strategic Design และ Context MappingContext Mappingกิจกรรมการระบุ Bounded Context และนิยามความสัมพันธ์ระหว่างกัน (เช่น Partnership, Shared Kernel, ACL) เพื่อจัดการการเชื่อมต่อระหว่างทีม/ระบบStrategic Design (Stage 2) → ลงมือสร้าง building blocks (Stage 3) → จัดสถาปัตยกรรมและรูปแบบขั้นสูง (Stage 4) → และฝึกวิจารณญาณว่าเมื่อไหร่ควร/ไม่ควรใช้ (Stage 5)
DDD ไม่ใช่ชุดกฎตายตัว แต่คือวิธีคิดที่เริ่มจาก “ภาษาและขอบเขต” ก่อน “code” — building blocks เป็นเพียงเครื่องมือ ส่วนคุณค่าจริงอยู่ที่การเข้าใจธุรกิจอย่างลึกซึ้งและจำลองมันออกมาให้ตรง
เจาะลึกแนวคิดในบทนี้ต่อได้ที่คลังอ้างอิง DevIQ:
- Anemic Domain Model — model ที่เก็บแต่ข้อมูลไม่มีพฤติกรรม เหมาะกับ subdomain แบบ CRUD ตามที่บทนี้แนะนำ แต่กลายเป็น anti-pattern ถ้าเผลอใช้กับ core domain ที่มีกฎซับซ้อน
- Aggregate — ทบทวนขอบเขตของ Aggregate ที่ถูกต้อง เพื่อหลีกเลี่ยง anti-pattern “Aggregate ใหญ่เกินไป” ที่บทนี้เตือนไว้
- Value Object — ทบทวนคุณสมบัติที่แท้จริงของ Value Object เพื่อไม่ให้ตกหลุม anti-pattern “Value Object ที่ผิดเพี้ยน”
- Big Ball of Mud — anti-pattern สถาปัตยกรรมที่บทนี้ยกเป็นตัวอย่างผลลัพธ์ของการไม่มีขอบเขตชัดเจน
ทดสอบรวบยอด — Stage 5
ข้อ 1 / 4งานใดที่ “ไม่ควร” ใช้ DDD เต็มรูปแบบ?