Domain-Driven Design คืออะไร?
Domain-Driven Design (DDD)Domain-Driven Design (DDD)แนวทางพัฒนาซอฟต์แวร์ที่เน้นสร้าง domain model ซึ่งเข้าใจกฎและกระบวนการของธุรกิจอย่างลึกซึ้ง แล้วเขียนลงใน code จริง บัญญัติโดย Eric Evans (2003) แบ่งเป็น Strategic และ Tactical DesignStrategic Design คือ “ปรัชญาการพัฒนาซอฟต์แวร์” ที่ Eric Evans บัญญัติไว้ในหนังสือเล่มสำคัญ Domain-Driven Design: Tackling Complexity in the Heart of Software (2003) เป็นแนวทางที่ช่วยให้ทีมจัดการกับการ สร้างและดูแลรักษา ซอฟต์แวร์สำหรับ problem domainDomainขอบเขตของปัญหาหรือธุรกิจที่ซอฟต์แวร์ของเราเข้าไปแก้ไข เช่น การขนส่งสินค้า การธนาคาร e-commerce — คือ “โลกของผู้ใช้” ที่โปรแกรมต้องเข้าใจStrategic Design ที่ซับซ้อนได้อย่างมีประสิทธิภาพ
บทนี้ปูพื้นภาพรวมของ DDD ทั้ง practices, patterns และ principles ก่อนที่เราจะลงรายละเอียดในบทต่อๆ ไป สิ่งที่คุณจะได้คือเข้าใจว่า ทำไมการวิเคราะห์ “พื้นที่ปัญหา” และการเลือกว่าจะทุ่มแรงตรงไหน จึงสำคัญ และทำไม การทำงานร่วมกัน การสื่อสาร และ context จึงเป็นหัวใจของการออกแบบซอฟต์แวร์ที่ดูแลรักษาได้
ความท้าทายของการสร้างซอฟต์แวร์สำหรับ domain ที่ซับซ้อน
หัวข้อที่มีชื่อว่า “ความท้าทายของการสร้างซอฟต์แวร์สำหรับ domain ที่ซับซ้อน”ก่อนจะเข้าใจว่า DDD ช่วยอะไร เราต้องเข้าใจก่อนว่าอะไรทำให้การสร้างซอฟต์แวร์ที่ไม่ธรรมดานั้นยาก น่าแปลกที่รูปแบบสถาปัตยกรรมที่ “พบบ่อยที่สุด” ใน app ธุรกิจ กลับเป็นรูปแบบที่เรียกว่า Big Ball of MudBig Ball of Mudระบบที่ปนเปกันยุ่งเหยิงไม่มีขอบเขตชัด ให้ลากเส้นล้อมไว้แล้วยอมรับสภาพ ไม่พยายามทำ model ละเอียดในนั้น และระวังไม่ให้ลามไป context อื่นStrategic Design (BBoM) — คำที่ Brian Foote และ Joseph Yoder นิยามไว้ว่า
ระบบที่โครงสร้างยุ่งเหยิง กระจัดกระจาย สะเปะสะปะ และถูกปะผุไปทีละจุด — ดงรกชัฏของ code สปาเกตตี (spaghetti code)
หนึ่งในสาเหตุหลักที่ซอฟต์แวร์กลายเป็นเรื่องซับซ้อนและดูแลยาก คือการ ปนกันระหว่างความซับซ้อนของ domain (ธุรกิจ) กับความซับซ้อนทางเทคนิค ดังรูปที่ 1-1

ความซับซ้อนมีสองชนิด: deliberate (ตั้งใจ) คือความซับซ้อนของกฎธุรกิจเองที่หลีกเลี่ยงไม่ได้ ส่วน accidental (โดยบังเอิญ) คือความซับซ้อนที่เราสร้างขึ้นเองจากทางเลือกทางเทคนิค เป้าหมายของ DDD คือแยกสองสิ่งนี้ออกจากกัน เพื่อให้ความซับซ้อนทางธุรกิจไม่จมหายไปในรายละเอียดทางเทคนิค
code ที่เกิดขึ้นโดยไม่มีภาษากลาง
หัวข้อที่มีชื่อว่า “code ที่เกิดขึ้นโดยไม่มีภาษากลาง”เมื่อทีมไม่ได้โฟกัสที่ภาษาและความรู้ร่วมกันเกี่ยวกับ problem domain ผลคือ codebase ที่ “ทำงานได้ แต่ไม่เปิดเผยเจตนาของธุรกิจ” code แบบนี้อ่านยาก แก้ยาก เพราะต้องคอยแปลไปมาระหว่าง Analysis ModelAnalysis Modelmodel เชิงวิเคราะห์ — ภาพการออกแบบเชิงตรรกะและโครงสร้างของซอฟต์แวร์ที่คนไม่ใช่สายเทคนิคก็เข้าใจได้ (เช่น ภาพร่างหรือ UML) ใน DDD จะถูกผูกเข้ากับ code model ผ่าน Ubiquitous LanguageStrategic Design กับ Code ModelCode Modelmodel ใน code — การแสดงออกของ domain model ใน code จริง DDD ถือว่า code model คือ “การแสดงออกหลัก” (primary expression) ของ domain model ไม่ใช่ไดอะแกรมหรือเอกสารStrategic Design ซึ่งทั้งแพงและพลาดง่าย เมื่อ code ไม่ผูกกับ model ที่ธุรกิจเข้าใจ มันจะค่อยๆ เสื่อมจนกลายเป็น BBoM ในที่สุด
ขาดการจัดระเบียบ
หัวข้อที่มีชื่อว่า “ขาดการจัดระเบียบ”ระบบที่เป็น BBoM ในช่วงแรกมักสร้างได้เร็วและประสบความสำเร็จดี แต่เพราะไม่ได้ออกแบบรอบ model ของ problem domain การต่อยอดในภายหลังจึงยุ่งยากขึ้นเรื่อยๆ ดังรูปที่ 1-2

เมื่อยังฝืนพัฒนาต่อบนสถาปัตยกรรมแบบสปาเกตตี ความเร็วในการเพิ่ม feature จะลดลงเรื่อยๆ version ใหม่เต็มไปด้วย bug และทีมเริ่มบ่นว่าทำงานด้วยยาก สุดท้ายแม้จะเพิ่มคนก็เร่ง velocity ไม่ขึ้น แล้วคำขอที่น่ากลัวที่สุดก็มาถึง — “เขียนใหม่ทั้งระบบ” ซึ่งบ่อยครั้งก็วนกลับไปสร้าง BBoM ก้อนใหม่ขึ้นมาอีก
ขาดการโฟกัสที่ problem domain
หัวข้อที่มีชื่อว่า “ขาดการโฟกัสที่ problem domain”project ซอฟต์แวร์ล้มเหลวเมื่อเราไม่เข้าใจ domain ของธุรกิจดีพอ การพิมพ์ code ไม่เคยเป็นคอขวด — ส่วนที่ยากจริงๆ คือการสร้างและรักษา model ที่ใช้งานได้จริง ของ domain ที่ตอบโจทย์ business use case ยิ่งคุณลงทุนทำความเข้าใจธุรกิจมากเท่าไร คุณก็ยิ่งจำลองมันลงในซอฟต์แวร์ได้แม่นยำขึ้นเท่านั้น
ทีมจำนวนมากทุ่มเวลาไปถกเรื่อง “ฐานข้อมูล” และ “framework” ตั้งแต่ก่อนจะเข้าใจธุรกิจ — นี่คือการเอา solution มาก่อน problem ผลลัพธ์คือ model ที่สะท้อนเพียงตารางในฐานข้อมูล ไม่ใช่กระบวนการธุรกิจ (business process) จริง
patterns ของ DDD จัดการความซับซ้อนอย่างไร
หัวข้อที่มีชื่อว่า “patterns ของ DDD จัดการความซับซ้อนอย่างไร”DDD รับมือกับทั้งสองเรื่องพร้อมกัน คือ การทำความเข้าใจ problem domain และ การสร้าง solution ที่ดูแลรักษาได้ โดยใช้ patterns สองกลุ่ม: strategic และ tactical
Strategic patterns — ลายแปรงกว้าง
หัวข้อที่มีชื่อว่า “Strategic patterns — ลายแปรงกว้าง”patterns เชิงกลยุทธ์Domain-Driven Design (DDD)แนวทางพัฒนาซอฟต์แวร์ที่เน้นสร้าง domain model ซึ่งเข้าใจกฎและกระบวนการของธุรกิจอย่างลึกซึ้ง แล้วเขียนลงใน code จริง บัญญัติโดย Eric Evans (2003) แบ่งเป็น Strategic และ Tactical DesignStrategic Design ทำหน้าที่กลั่น problem domain และกำหนดรูปร่างสถาปัตยกรรมของ app ประกอบด้วยแนวคิดหลัก:
- กลั่น problem domain เพื่อเผยสิ่งที่สำคัญ — ไม่ใช่ทุกส่วนของระบบที่ต้องออกแบบอย่างสมบูรณ์แบบ การกลั่น domain ใหญ่ออกเป็น SubdomainSubdomainส่วนย่อยของ Domain ใหญ่ เช่น Domain e-commerce แตกเป็น subdomain การสั่งซื้อ การชำระเงิน คลังสินค้า ฯลฯ อยู่ใน “พื้นที่ปัญหา” (problem space)Strategic Design ย่อยๆ จะเผยให้เห็น Core DomainCore Domainsubdomain ที่สำคัญและสร้างความได้เปรียบในการแข่งขันมากที่สุด เป็นเหตุผลที่เราต้องสร้างซอฟต์แวร์เองแทนการซื้อ ควรทุ่มทีมเก่งที่สุดลงตรงนี้Strategic Design ซึ่งเป็นเหตุผลที่แท้จริงว่าทำไมระบบนี้ถึงถูกสร้างขึ้น และเป็นจุดที่ควรทุ่มทีมเก่งที่สุดลงไป
- สร้าง ModelModelระบบของแนวคิดที่อธิบายแง่มุมสำคัญของ Domain เพื่อใช้แก้ปัญหา ไม่ใช่ภาพสะท้อนความจริงทั้งหมด แต่เป็น “การตีความที่มีประโยชน์” ของความจริงStrategic Design เพื่อแก้ปัญหาของ domain — model ไม่ใช่ภาพสะท้อนชีวิตจริงทั้งหมด แต่เป็น “สิ่งที่สร้างขึ้นอย่างพอเหมาะ” เพื่อตอบ business use case โดยยังคงกฎและตรรกะของธุรกิจไว้ และแยกออกจาก infrastructure code
- ใช้ภาษากลางเพื่อให้ร่วมสร้าง model ได้ — Ubiquitous LanguageUbiquitous Languageภาษากลางที่ทุกคนในทีม (ทั้งนักพัฒนาและผู้เชี่ยวชาญธุรกิจ) ใช้ร่วมกัน อิงกับ domain model โดยตรง คำเดียวกันต้องหมายถึงสิ่งเดียวกัน และสะท้อนลงใน codeStrategic Design (UL) คือภาษาที่วิวัฒน์ไปเรื่อยๆ ซึ่ง domain expert และนักพัฒนาใช้ร่วมกัน คำเดียวกันต้องหมายถึงสิ่งเดียวกัน และสะท้อนตรงลงไปในโครงสร้างของ code
เมื่อ model ใหญ่ขึ้นจนภาษาเริ่มกำกวม เราใช้ Bounded ContextBounded Contextขอบเขตที่ชัดเจน (มักเป็นระบบย่อยหรือทีมหนึ่ง) ที่ model หนึ่งใช้ได้และมีความหมายแน่นอน ภายในขอบเขตนี้ Ubiquitous Language สอดคล้องกัน 100%Strategic Design มาล้อม model แต่ละก้อนไว้ เพื่อกันไม่ให้แต่ละ model ปนเปื้อนกันจนกลายเป็น BBoM รูปที่ 1-3 แสดงให้เห็นว่า strategic patterns ถูกนำมาใช้จัดระเบียบ problem domain ขนาดใหญ่และปกป้อง model ย่อยอย่างไร (ลองเทียบกับรูปที่ 1-2)

นอกจากนี้ DDD ยังเน้นการเข้าใจ ความสัมพันธ์ระหว่าง context ผ่าน Context MapContext Mapแผนภาพที่แสดง Bounded Context ทั้งหมดและความสัมพันธ์ระหว่างกัน ช่วยให้เห็นภาพรวมและจุดเชื่อมต่อ/แปลภาษาในระบบใหญ่Strategic Design ซึ่งช่วยให้ทีมเห็นภาพใหญ่ว่า model ไหนรับผิดชอบอะไร และแลกเปลี่ยนข้อมูลกันอย่างไรเพื่อทำให้กระบวนการธุรกิจที่ข้ามหลาย subdomain สำเร็จ
Tactical patterns — ลายแปรงละเอียด
หัวข้อที่มีชื่อว่า “Tactical patterns — ลายแปรงละเอียด”patterns เชิงยุทธวิธีDomain-Driven Design (DDD)แนวทางพัฒนาซอฟต์แวร์ที่เน้นสร้าง domain model ซึ่งเข้าใจกฎและกระบวนการของธุรกิจอย่างลึกซึ้ง แล้วเขียนลงใน code จริง บัญญัติโดย Eric Evans (2003) แบ่งเป็น Strategic และ Tactical DesignStrategic Design หรือ “building blocks” คือชุดของ pattern ที่ใช้สร้าง model ที่ทรงพลังภายใน bounded context หนึ่งๆ หลายตัวถูกบันทึกไว้แล้วโดย Martin Fowler ใน Patterns of Enterprise Application Architecture และ Erich Gamma และคณะใน Design Patterns (GoF) — pattern เหล่านี้ไม่ได้เหมาะกับทุก model ต้องเลือกใช้ตามความเหมาะสม (เราจะลงลึกใน Part III)
Problem Space และ Solution Space
หัวข้อที่มีชื่อว่า “Problem Space และ Solution Space”วิธีคิดสำคัญของ DDD คือการแยกระหว่าง “ปัญหา” กับ “คำตอบ”
Problem SpaceProblem Space“พื้นที่ปัญหา” — สิ่งที่ธุรกิจต้องการแก้ไข อยู่ในรูปของ Domain และ Subdomain (ยังไม่พูดถึงวิธีแก้)Strategic Design คือสิ่งที่ธุรกิจต้องการแก้ — เรากลั่น domain ใหญ่ออกเป็น subdomain ที่จัดการได้ง่ายขึ้น เป้าหมายในพื้นที่นี้คือ เผยว่าอะไรสำคัญและควรโฟกัสตรงไหน ดังรูปที่ 1-4

Solution SpaceSolution Space“พื้นที่คำตอบ” — วิธีที่เราเลือกแก้ปัญหา อยู่ในรูปของ Bounded Context และ model 1 subdomain อาจมีได้หลายคำตอบStrategic Design คือวิธีที่เราเลือกแก้ปัญหา — เป็นที่อยู่ของ bounded context และ model ใน code ที่กำหนดรูปร่างสถาปัตยกรรมของ app ให้ดูแลรักษาง่าย ดังรูปที่ 1-5

DDD คือชุดของ patterns, principles และ practices สำหรับจัดการความซับซ้อน โดยแบ่งสนามออกเป็นสองพื้นที่: problem space (กลั่น domain ให้เห็นว่าอะไรสำคัญ) และ solution space (สร้าง model ที่ดูแลรักษาได้) จำหลักง่ายๆ ว่า strategic patterns ใช้ได้กับเกือบทุก app ส่วน tactical patterns จะคุ้มค่าก็ต่อเมื่อ model มีตรรกะธุรกิจที่เข้มข้นพอ
practices และ principles ที่เป็นแก่นแท้
หัวข้อที่มีชื่อว่า “practices และ principles ที่เป็นแก่นแท้”เหนือกว่าตัว pattern คือ practices และหลักการที่มักถูกมองข้ามเพราะคนไปโฟกัสที่ tactical pattern มากเกินไป:
- โฟกัสที่ Core DomainCore Domainsubdomain ที่สำคัญและสร้างความได้เปรียบในการแข่งขันมากที่สุด เป็นเหตุผลที่เราต้องสร้างซอฟต์แวร์เองแทนการซื้อ ควรทุ่มทีมเก่งที่สุดลงตรงนี้Strategic Design — ทุ่มแรงมากที่สุดกับส่วนที่สร้างความได้เปรียบในการแข่งขัน
- เรียนรู้ผ่านการทำงานร่วมกัน — ถ้าไม่มี domain expert เข้ามาร่วมอย่างจริงจัง การถ่ายทอดความรู้ก็ไม่มีทางเกิดขึ้น
- สร้าง model ผ่านการสำรวจและทดลอง — Evans บอกว่า “สำหรับทุกการออกแบบที่ดี ต้องมีการออกแบบที่แย่อย่างน้อยสามแบบมาก่อน” อย่าหยุดที่ model แรกที่ใช้ได้
- การสื่อสาร — สิ่งสำคัญที่สุดเพียงสิ่งเดียวของ DDD คือการสร้าง UL ถ้าไม่มีภาษากลาง การทำงานร่วมกันเพื่อแก้ปัญหาก็เกิดไม่ได้
- เข้าใจขอบเขตการใช้งานของ model — แต่ละ model มีความหมายเฉพาะภายใน bounded context ของมัน
- วิวัฒน์ model ตลอดเวลา — business case ใหม่ๆ อาจทำให้ model เดิมพังหรือต้องทำให้แนวคิดชัดเจนขึ้น
ตลอดคอร์สนี้เราจะใช้ตัวอย่าง ระบบขนส่งตู้ container (Cargo Shipping) เดียวกับตำราของ Evans บริษัทขนส่งต้องการระบบที่ “จอง” การขนส่งจากต้นทางถึงปลายทางตามกำหนด วาง “แผนเดินทาง” ผ่านท่าเรือและเรือหลายลำ และ “ติดตามสถานะ” ของสินค้า ฟังดูเหมือนงาน CRUD ทั่วไป แต่กฎธุรกิจซับซ้อนกว่าที่คิดมาก — นี่แหละคือสนามที่ DDD ได้เปล่งประกาย
อีกตัวอย่างที่คุ้นเคยกว่าคือ e-commerce เช่น “คำสั่งซื้อ (Order)” ที่มี core domain (กระบวนการสั่งซื้อ/ราคา) แยกจาก generic subdomain (login, การแจ้งเตือน) เราจะเทียบเคียงสองโลกนี้ไปตลอดทั้งคอร์ส เพื่อให้เห็นภาพชัดยิ่งขึ้น
ความเข้าใจผิดที่พบบ่อยเกี่ยวกับ DDD
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อยเกี่ยวกับ DDD”- “tactical patterns คือหัวใจของ DDD” — ผิด คุณ implement DDD ได้โดยไม่ต้องมี rich domain modelRich Domain Modelmodel domain ที่ “รวยพฤติกรรม” — object ที่เก็บทั้งข้อมูลและ business logic ไว้ด้วยกัน เป็นคู่ตรงข้ามของ Anemic Domain Model (ที่มีแต่ getter/setter แล้วผลัก logic ไปไว้ที่อื่น) tactical patterns ของ DDD จะคุ้มค่าก็ต่อเมื่อ model “รวย” พอเช่นนี้Tactical Design หรือ repositoryRepositoryตัวที่ทำให้เราเข้าถึง Aggregate ราวกับเป็น collection ในหน่วยความจำ ซ่อนรายละเอียดฐานข้อมูล มี1 repository ต่อ1 Aggregate RootTactical Design ด้วยซ้ำ DDD เป็นเรื่องของการแก้ปัญหาผ่านการร่วมมือมากกว่าตัว design pattern
- “DDD คือ framework” — ผิด DDD ไม่ต้องใช้ framework หรือฐานข้อมูลพิเศษ model ใน code ยึดหลัก POCO/POJO (ปลอดจาก infrastructure) และเป็น architecturally agnostic — แต่ละ bounded context เลือกสถาปัตยกรรมของตัวเองได้
- “DDD คือกระสุนเงิน (silver bullet)” — ผิด DDD ใช้แรงมาก ต้องมีฝ่ายธุรกิจที่ร่วมมือและทีมที่ทุ่มเท จึง ไม่คุ้ม กับ domain ง่ายๆ อย่าง block ทั่วไปที่แทบไม่มีตรรกะธุรกิจ
DDD ไม่ใช่ pattern language แต่เป็น ปรัชญาที่มุ่งส่งมอบคุณค่าผ่านการสื่อสารและความร่วมมือ โดยมีภาษาและ domain เป็นศูนย์กลาง ส่วนซอฟต์แวร์เป็นเพียง “ผลพลอยได้” (artifact) ของกระบวนการนั้น
เจาะลึกแนวคิดในบทนี้ต่อได้ที่คลังอ้างอิง DevIQ:
- Strategic Design — ขยายความ strategic patterns ที่บทนี้แนะนำ (subdomain, model, ubiquitous language, bounded context)
- Tactical Design — ภาพรวมของ tactical patterns/building blocks ที่บทนี้เกริ่นไว้ก่อนลงรายละเอียดใน Part III
- Subdomain — อธิบายการกลั่น domain ใหญ่ออกเป็น subdomain และการหา core domain ตามที่บทนี้กล่าวถึง
- Bounded Context — ขยายความเรื่องขอบเขตที่ล้อมแต่ละ model ไว้ไม่ให้ปนกันจนกลายเป็น BBoM
- Ubiquitous Language — รายละเอียดของภาษากลางที่บทนี้ชี้ว่าเป็นหัวใจของการสื่อสารใน DDD
เช็กความเข้าใจ — บทที่ 1
ข้อ 1 / 4ตามหนังสือ อะไรคือสาเหตุหลักที่ทำให้ซอฟต์แวร์ซับซ้อนจนกลายเป็น Big Ball of Mud?