Subdomains
พื้นที่ความรู้ย่อยที่นิยามชัดเจนภายใน domain ใหญ่
Subdomain คือ พื้นที่ความรู้หรือความรับผิดชอบย่อยที่นิยามไว้ชัดเจน ภายใน domain ที่ใหญ่กว่า เกิดจากการแตก domain ทั้งก้อน (เช่น “การค้าปลีกออนไลน์”) ออกเป็นส่วนย่อยที่มี cohesion สูงและมีขอบเขตชัดเจนจากส่วนอื่น เช่น “การจัดการสินค้า”, “การชำระเงิน”, “การจัดส่ง” แต่ละ subdomain ยังแตกย่อยลงไปอีกได้เรื่อย ๆ ตามความซับซ้อนของธุรกิจ
จุดที่มักสับสน คือความสัมพันธ์ระหว่าง subdomain กับ Bounded Context — subdomain เป็นส่วนหนึ่งของ problem space (ปัญหาทางธุรกิจที่มีอยู่จริง ไม่ว่าเราจะเขียน code หรือไม่) ส่วน bounded context เป็นส่วนหนึ่งของ solution space (ขอบเขตของ model ซอฟต์แวร์ที่เราออกแบบขึ้นเพื่อตอบปัญหานั้น) ทีมงานค้นพบ subdomain ได้จากการวิเคราะห์ธุรกิจ เช่น event storming หรือ domain storytelling ก่อนที่จะตัดสินใจว่าจะสร้าง bounded context ใดมารองรับ ในทางอุดมคติ subdomain หนึ่งควรตรงกับ bounded context หนึ่ง แต่ในความเป็นจริงบางครั้ง bounded context เดียวอาจตอบสนองหลาย subdomain (มักเป็นสัญญาณของขอบเขตที่ยังไม่ลงตัว) หรือ subdomain เดียวอาจถูกรองรับด้วยหลาย bounded context ก็ได้
การค้นหาขอบเขตของ subdomain มักอาศัยสัญญาณจากภาษา (คำศัพท์ทางธุรกิจเริ่มเปลี่ยนความหมายหรือเริ่มไม่เกี่ยวข้องกัน), จากความสัมพันธ์เชิงหน้าที่ (function ไหนผูกติดกันแน่นเป็นกลุ่มเดียวกัน) และจากโครงสร้างองค์กร (แผนกหรือทีมที่ดูแลความรับผิดชอบนั้นอยู่แล้ว) Microsoft Learn แนะนำให้เริ่มจากการร่างแผนภาพความสัมพันธ์ของ function ทางธุรกิจทั้งหมดแบบหยาบ ๆ ก่อน แล้วค่อยมองหากลุ่มก้อนที่มี cohesion สูงเพื่อตัดออกมาเป็น subdomain แต่ละอัน
บทบาทใน DDD
หัวข้อที่มีชื่อว่า “บทบาทใน DDD”Subdomain เป็นเครื่องมือหลักของ strategic design ใน DDD ใช้ตอบคำถามว่า “ควรทุ่มความพยายามในการออกแบบและลงทุนทีมที่เก่งที่สุดไปที่ส่วนไหนของระบบ” Eric Evans และ Vaughn Vernon แบ่งประเภทของ subdomain ไว้ 3 แบบ:
- Core subdomain: พื้นที่หลักที่สร้างความได้เปรียบในการแข่งขัน เป็นเหตุผลที่องค์กรมีอยู่และแตกต่างจากคู่แข่ง ควรได้รับความสนใจสูงสุด ทีมที่เก่งที่สุด และความพยายามในการออกแบบ (distillation) มากที่สุด
- Supporting subdomain: จำเป็นต่อความสำเร็จขององค์กร แต่ไม่ใช่จุดสร้างความแตกต่าง มักต้องพัฒนาแบบ custom เพราะไม่มีโซลูชันสำเร็จรูปที่เหมาะสม แต่ core domain จะทำงานไม่ได้ถ้าขาดส่วนนี้
- Generic subdomain: จำเป็นต่อการดำเนินงานแต่ไม่สร้างความได้เปรียบใด ๆ อุตสาหกรรมมักแก้ปัญหานี้ไปแล้ว จึงนำกลับมาใช้ซ้ำ ซื้อสำเร็จรูป หรือ outsource ได้ เช่น authentication/authorization, payment processing, messaging, user identity management
การจำแนกประเภทนี้ช่วยจัดลำดับความสำคัญของทรัพยากร ทีมที่เก่งที่สุดควรอยู่กับ core subdomain ส่วน generic subdomain ควรใช้ของสำเร็จรูปแทนการเขียนเองเพื่อประหยัดความพยายามไปลงกับสิ่งที่สร้างมูลค่าจริง กระบวนการทำงานทั่วไปคือ วิเคราะห์ domain ธุรกิจ (มักเริ่มจาก event storming) เพื่อค้นหาว่ามี subdomain อะไรบ้างและแต่ละอันคือ core, supporting หรือ generic จากนั้นจึงกำหนด bounded context และ context map ตามมาในขั้น solution space
Vaughn Vernon ใน Implementing Domain-Driven Design เสนอแนวคิด “Core Domain Chart” คือการให้ทีมประเมิน subdomain แต่ละอันบนสองแกน ได้แก่ ระดับความแตกต่างทางธุรกิจ (business differentiation) กับระดับความยุ่งเหยิงของ model ที่มีอยู่ (model chaos) subdomain ที่แตกต่างสูงแต่ model ยังไม่ชัดเจน คือผู้สมัคร core domain ที่ควรได้รับการลงทุนออกแบบและ distillation มากที่สุด ในขณะที่ subdomain ที่แตกต่างต่ำควรถูกผลักไปเป็น supporting หรือ generic แล้วปล่อยให้ทีมภายนอกหรือโซลูชันสำเร็จรูปดูแลแทน
ตัวอย่าง
หัวข้อที่มีชื่อว่า “ตัวอย่าง”Microsoft Learn ยกตัวอย่างบริการส่งของด้วยโดรนของบริษัทสมมติ Fabrikam ซึ่งวิเคราะห์ domain ออกมาได้ subdomain ดังนี้: Shipping และ Drone management เป็น core subdomain เพราะเป็นหัวใจของธุรกิจ, Invoicing เป็น supporting subdomain เพราะต้องพัฒนาเองแต่ไม่ใช่จุดขาย, ส่วน User accounts และ Call center เป็น generic subdomain เพราะมีโซลูชันสำเร็จรูปให้ใช้ได้เลย
graph TD
Domain[Drone Delivery Domain]
Domain --> Shipping[Shipping Core]
Domain --> DroneMgmt[Drone Management Core]
Domain --> Invoicing[Invoicing Supporting]
Domain --> UserAccounts[User Accounts Generic]
Shipping --> ShippingBC[Shipping Bounded Context]
DroneMgmt --> DroneBC[Drone Management Bounded Context]
Invoicing --> InvoicingBC[Invoicing Bounded Context]
UserAccounts --> IdentityBC[Identity Bounded Context]
ตัวอย่าง code C# ด้านล่างจำลองการทำ “domain map” เป็นข้อมูลใน code เพื่อสื่อสารให้ทั้งทีมเห็นตรงกันว่าแต่ละ bounded context สังกัด subdomain ประเภทใด และควรได้รับความสนใจมากน้อยเพียงใด:
// ประเภทของ subdomain ตามการจำแนกของ Evans/Vernonpublic enum SubdomainType{ Core, Supporting, Generic}
// แผนที่ subdomain หนึ่งรายการ พร้อมรายชื่อ bounded context ที่รองรับ subdomain นั้นpublic sealed record SubdomainMap( string Name, SubdomainType Type, IReadOnlyList<string> BoundedContexts);
// ผลจากการวิเคราะห์ domain ของ Fabrikam ก่อนตัดสินใจเรื่องสถาปัตยกรรมใด ๆpublic static class FabrikamDomainMap{ public static readonly IReadOnlyList<SubdomainMap> Subdomains = new[] { new SubdomainMap("Shipping", SubdomainType.Core, new[] { "Ordering", "Delivery" }), new SubdomainMap("DroneManagement", SubdomainType.Core, new[] { "FleetControl" }), new SubdomainMap("Invoicing", SubdomainType.Supporting, new[] { "Billing" }), new SubdomainMap("UserAccounts", SubdomainType.Generic, new[] { "Identity" }), };
// ใช้จัดลำดับว่าทีมเก่ง ๆ ควรถูกจัดสรรไปที่ subdomain ไหนก่อน public static IEnumerable<SubdomainMap> ByInvestmentPriority() => Subdomains.OrderBy(s => s.Type); // Core มาก่อน Supporting มาก่อน Generic}code นี้ไม่ใช่ pattern ทางเทคนิคที่ต้อง implement ตรง ๆ แต่เป็นตัวอย่างว่าการตัดสินใจเชิงกลยุทธ์ (strategic design) สามารถทำให้ชัดเจนและตรวจสอบได้ในระดับ code เช่น การเขียน test ที่ยืนยันว่า generic subdomain อย่าง UserAccounts ไม่ได้ถูกพัฒนาเองทั้งหมดโดยทีม core
ความสัมพันธ์กับแนวคิดอื่น
หัวข้อที่มีชื่อว่า “ความสัมพันธ์กับแนวคิดอื่น”- Subdomain vs. Bounded Context: subdomain อยู่ใน problem space ส่วน bounded context อยู่ใน solution space ทีมมักค้นพบ subdomain ก่อนแล้วจึงออกแบบ bounded context ตาม แต่ทั้งสองไม่จำเป็นต้องตรงกันแบบ 1 ต่อ 1 เสมอไป
- Subdomain vs. Domain: Domain คือภาพรวมทั้งหมดของปัญหาธุรกิจ ส่วน subdomain คือส่วนย่อยที่แตกออกมาเพื่อให้จัดการความซับซ้อนได้ง่ายขึ้น
- Core subdomain กับ Distillation: Evans ใช้กระบวนการ distillation เพื่อกลั่นกรองแยก core domain ออกจาก generic subdomain และ cohesive mechanism ให้ทีมโฟกัสพลังงานไปที่ส่วนที่สร้างมูลค่าจริง
- Generic subdomain กับ Anti-Corruption Layer: เมื่อ generic subdomain ถูก outsource หรือซื้อสำเร็จรูป มักต้องมี Anti-Corruption Layer กันไม่ให้ model ภายนอกรั่วไหลเข้ามาปนกับ core domain
- Subdomain กับ Context Mapping: หลังระบุ subdomain และแปลงเป็น bounded context แล้ว ทีมยังต้องทำ Context Mapping เพื่อบันทึกความสัมพันธ์และรูปแบบการรวมกัน (integration) ระหว่าง bounded context เหล่านั้น
- Subdomain กับ Ubiquitous Language: แต่ละ subdomain (และ bounded context ที่รองรับมัน) มักมีภาษากลางของตัวเองที่แตกต่างกัน เช่น คำว่า “account” ใน subdomain การเงินอาจหมายถึงบัญชีเงิน ในขณะที่ subdomain บัญชีผู้ใช้อาจหมายถึงตัวตนของผู้ใช้ระบบ การแยก subdomain ให้ชัดจึงช่วยลดความกำกวมของภาษาไปในตัว