ข้ามไปยังเนื้อหา

เมื่อไหร่ไม่ควรใช้ DDD

สิ่งที่แยกผู้ใช้ DDD ที่เก่งออกจากคนที่ “ทำตามสูตร” คือวิจารณญาณว่า “เมื่อไหร่ไม่ควรใช้” DDD มีต้นทุน และไม่ใช่คำตอบสำหรับทุกปัญหา

  • domain ซับซ้อน มีกฎธุรกิจซับซ้อนและเปลี่ยนแปลงบ่อย (อีคอมเมิร์ซ การเงิน ERP โลจิสติกส์ ประกันภัย)
  • ต้องการความร่วมมือใกล้ชิดระหว่างฝ่ายธุรกิจกับฝ่ายเทคนิค
  • ส่วนนั้นเป็น Core DomainCore Domainsubdomain ที่สำคัญและสร้างความได้เปรียบในการแข่งขันมากที่สุด เป็นเหตุผลที่เราต้องสร้างซอฟต์แวร์เองแทนการซื้อ ควรทุ่มทีมเก่งที่สุดลงตรงนี้Strategic Design ที่สร้างความได้เปรียบในการแข่งขัน

ถ้าเป็นแอป CRUD ง่าย ๆ (หน้า admin, รายงานข้อมูล, to-do list, ระบบจัดการรายชื่อ) การฝืนใช้ AggregateAggregateกลุ่มของ Entity และ Value Object ที่ถูกมองเป็นหนึ่งหน่วยเดียวเพื่อรักษาความถูกต้องของข้อมูล มีขอบเขตชัดเจน และเป็นหน่วยของ transactionTactical Design, Domain EventDomain Eventสิ่งที่ “เกิดขึ้นแล้ว” ใน domain ซึ่งส่วนอื่นสนใจ แทนด้วยอ็อบเจ็กต์ที่เปลี่ยนแปลงไม่ได้ ตั้งชื่อเป็นอดีต เช่น CargoWasRouted ใช้สื่อสารข้าม AggregateTactical Design, RepositoryRepositoryตัวที่ทำให้เราเข้าถึง Aggregate ราวกับเป็นคอลเลกชันในหน่วยความจำ ซ่อนรายละเอียดฐานข้อมูล มีหนึ่ง repository ต่อหนึ่ง Aggregate RootTactical Design เต็มรูปแบบจะเพิ่มความซับซ้อนโดยไม่จำเป็น ทำให้พัฒนาช้าลงและแก้ฟีเจอร์ง่าย ๆ ยากขึ้น — กลายเป็นโค้ดเบสที่ซับซ้อนเกินความต้องการจริงและต้นทุนบานปลาย

ทางสายกลาง

ในระบบจริงหนึ่งระบบ มักมีทั้งสองแบบปนกัน เช่น (อ้างอิงตัวอย่าง eShopOnContainers ของ Microsoft) microservice การสั่งซื้อใช้ DDD เต็มรูปแบบเพราะกฎซับซ้อน ส่วน microservice แคตตาล็อกที่เป็น CRUD ล้วนใช้ Anemic Domain ModelAnemic Domain Modelanti-pattern ที่อ็อบเจ็กต์มีแต่ getter/setter ไร้พฤติกรรม ตรรกะถูกดึงไปไว้ใน service ทั้งหมด — จ่ายค่า domain model แต่ไม่ได้ประโยชน์ของมันเลยTactical Design ก็เพียงพอ — เลือกใช้เครื่องมือตามความซับซ้อนของแต่ละส่วน


  • ภาษาไม่สอดคล้อง — คำเดียวกันหมายถึงคนละสิ่ง โดยไม่มี Bounded ContextBounded Contextขอบเขตที่ชัดเจน (มักเป็นระบบย่อยหรือทีมหนึ่ง) ที่ model หนึ่งใช้ได้และมีความหมายแน่นอน ภายในขอบเขตนี้ Ubiquitous Language สอดคล้องกัน 100%Strategic Design มากำหนดขอบเขต
  • Over-engineering งานง่าย — ยกชุดเครื่องมือ tactical ทั้งหมดมาใส่ CRUD ธรรมดา
  • [[Value Object]] ที่ผิดเพี้ยน — ใส่ identity หรือทำให้แก้ค่าได้ / EntityEntityอ็อบเจ็กต์ที่มี “ตัวตน” (identity) ต่อเนื่องตามเวลา แม้ค่าภายในเปลี่ยน ก็ยังเป็นสิ่งเดิม เช่น ลูกค้า, คำสั่งซื้อ — แยกแยะด้วย id ไม่ใช่ด้วยค่าTactical Design ที่สร้างโดยไม่มี identity ชัดเจน
  • Service อ้วน — ดึงตรรกะออกจาก model จนกลายเป็น Anemic Domain ModelAnemic Domain Modelanti-pattern ที่อ็อบเจ็กต์มีแต่ getter/setter ไร้พฤติกรรม ตรรกะถูกดึงไปไว้ใน service ทั้งหมด — จ่ายค่า domain model แต่ไม่ได้ประโยชน์ของมันเลยTactical Design
  • [[Aggregate]] ใหญ่เกินไป หรืออ้างอิง Aggregate อื่นโดยตรง → transaction ข้ามขอบเขต ปัญหาประสิทธิภาพและความถูกต้อง
  • กระโดดไป tactical ก่อน — ลงมือสร้าง Entity/Repository ก่อนเข้าใจ subdomain และขอบเขต
  • ละเลย Core — ทำ model ส่วนรอบนอกอย่างประณีต แต่ปล่อยส่วนหัวใจของธุรกิจไว้
  • [[Big Ball of Mud]] — Aggregate ปนเปกันหมด ไม่มีขอบเขตชัด โค้ดผูกกันแน่น
ลำดับที่ถูกต้อง

เริ่มจาก Strategic ก่อนเสมอ: เข้าใจ DomainDomainขอบเขตของปัญหาหรือธุรกิจที่ซอฟต์แวร์ของเราเข้าไปแก้ไข เช่น การขนส่งสินค้า การธนาคาร อีคอมเมิร์ซ — คือ “โลกของผู้ใช้” ที่โปรแกรมต้องเข้าใจStrategic Design → แบ่ง SubdomainSubdomainส่วนย่อยของ Domain ใหญ่ เช่น Domain อีคอมเมิร์ซ แตกเป็น 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 ราวกับเป็นคอลเลกชันในหน่วยความจำ ซ่อนรายละเอียดฐานข้อมูล มีหนึ่ง repository ต่อหนึ่ง Aggregate RootTactical Design ทันทีเป็นสาเหตุของปัญหาเชิงโครงสร้างที่แก้ยากภายหลัง


คุณได้เดินครบทั้ง 5 ขั้น: เข้าใจ “ทำไม” (Stage 0) → สร้าง Ubiquitous LanguageUbiquitous Languageภาษากลางที่ทุกคนในทีม (ทั้งนักพัฒนาและผู้เชี่ยวชาญธุรกิจ) ใช้ร่วมกัน อิงกับ domain model โดยตรง คำเดียวกันต้องหมายถึงสิ่งเดียวกัน และสะท้อนลงในโค้ดStrategic 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 ไม่ใช่ชุดกฎตายตัว แต่คือวิธีคิดที่เริ่มจาก “ภาษาและขอบเขต” ก่อน “โค้ด” — building blocks เป็นเพียงเครื่องมือ ส่วนคุณค่าจริงอยู่ที่การเข้าใจธุรกิจอย่างลึกซึ้งและจำลองมันออกมาให้ตรง


[quiz: ทดสอบรวบยอด — Stage 5 — coming soon]