สรุป Class Domain-Driven Design by Roofimon

เนื่องจากโชคดีซื้อตั๋ว class นี้ได้ทัน เลยอยากสรุปว่าได้เรียนอะไรจาก class นี้บ้าง

ในบทความนี้จะ focus ที่วิธีการ design ระบบโดยใช้หลัก domain-driven design ครับ ส่วนใครที่สนใจว่า DDD มีที่มาและมาช่วยแก้ปัญหาอะไร สามารถอ่านได้ในสรุปของพี่ปุ๋ยใน http://www.somkiat.cc/part-1-rise-of-domain-driven-design/ ครับ

การทำ domain-driven design ประกอบด้วย 2 ขั้นตอนหลักๆคือ strategic design และ tactical design โดยผู้ที่เข้าร่วมในการ design มักมี 2 role ได้แก่

  1. Developer — จะรู้รายละเอียดในด้านการพัฒนาระบบ เน้นไปทางภาษา tech
  2. Domain Expert — จะรู้รายละเอียดในด้านธุรกิจ เน้นไปทางภาษา Business

Strategic Design

Strategic Design เป็นการ design ภาพกว้างของระบบทั้งหมดเพื่อให้เห็นว่าระบบมีส่วนประกอบต่างๆอย่างไรบ้าง ก่อนจะลงรายละเอียดในขั้นตอนต่อไป

ขั้นตอนในการทำ Strategic Design

1. ทำการแจกแจง function ทั้งหมดในระบบออกมาพร้อมทั้งระบุ entity ที่ใช้และจัดกลุ่มเป็น Bounded Context โดยใช้ ubiquitous language

Bounded Context
ในระบบ 1 problem domain มักมีหลายส่วนเชื่อมโยงกันอยู่ การสร้าง Bounded Context จะเป็นการแบ่งส่วนต่างๆให้ออกมาชัดเจนซึ่งสามารถแบ่งได้หลายวิธี เช่น
แบ่งจาก role e.g. โรงพยาบาลจะถูกแบ่งเป็น หมอ,พยาบาล, เจ้าหน้าที่การเงิน
แบ่งจาก function e.g. ระบบ ecommerce จะแบ่งเป็นระบบแสดงสินค้า, ระบบการตะกร้าสินค้า, ระบบการสั่งซื้อ
Bounded Context ต่างกันจะมี Entity เดียวกันได้ โดยจะใช้ข้อมูลใน Entity ต่างกัน เช่น ใน context ของหมอกับเจ้าหน้าที่การเงินอาจเกี่ยวข้องกับ Entity ของยาทั้งคู่ แต่หมอจะใช้ข้อมูลในส่วนการสั่งยารักษา แต่เจ้าหน้าที่การเงินจะใช้ข้อมูลในส่วนราคาของยา
แต่ละ bounded context ต้องสามารถทำงานได้ด้วยตัวเองแม้ context อื่นที่เกี่ยวข้องจะเกิดปัญหา เช่น ติดต่อรับส่งข้อมูลไม่ได้
ubiquitous language
เนื่องจากใน design จะมีบุคคลที่มีความรู้ในด้านที่ต่างกันมากได้แก้ developers และ domain expert มาประชุมร่วมกัน ดังนั้นการ design จึงต้องใช้ภาษาที่ทุกคนสามารถเข้าใจได้ตรงกัน
ขั้นตอนนี้เหมือนเป็นการสร้างรากฐานให้กับขั้นตอนต่อๆไป ดังนั้นการสร้าง bounded context ที่ครบถ้วนชัดเจนจึงสำคัญมาก

2. จัดแบ่งประเภทของ Bounded Context ทั้งหมด

Bounded Context ในระบบจะสามารถแบ่งออกได้เป็น 3 ประเภท

  1. Core Domain เป็นส่วนที่สำคัญที่สุดของระบบในด้าน Business โดย Domain Expert จะเป็นผู้กำหนดว่า domain ไหนเป็น core domain ในปัจจุบัน resource ในการ implement จะลงที่ส่วนนี้เป็นหลัก
  2. Supporting subdomain เป็น domain ที่ต้อง implement เอง แต่ไม่ได้มี business value มากเท่า Core domain สามารถใช้ outsource พัฒนาส่วนนี้แทน แล้วส่งคนไปเพื่อศึกษาระบบ สามารถเปลี่ยนเป็น core domain ได้ในอนาคต
  3. Generic subdomain เป็น domain ที่สามารถซื้อ off the shelf software มาใช้งานได้ โดยให้ PM ไปดูแลให้ vendor ปรับ software ให้ตรงตาม requirement ของระบบ

3. สร้าง context mapping ระหว่างแต่ละ domain

ในแต่ละ domain มักจะมีการใช้ Entity ที่เป็นของ domain อื่นอยู่เสมอดังที่ได้กล่าวไปขั้นต้น ในขั้นตอนนี้จะเป็นการระบุให้ชัดเจนว่ามี domain ไหนที่เชื่อมโยงกันบ้างและเชื่อมโยงไปในทิศทางไหน

ความสัมพันธ์ระหว่างแต่ละ domain สามารถมีได้หลายประเภท เช่น

Customer-Supplier

เป็นความสัมพันธ์ระหว่าง 2 bounded context โดยให้ supplier เป็น upstream ส่งข้อมูลให้กับ customer เป็น downstream โดย supplier จะเป็นผู้กำหนดว่าจะส่งข้อมูลให้เท่าไรในรูปแบบใด ความสัมพันธ์นี้มักใช้กับการส่งข้อมูลทั่วไปที่สามารถขอให้ supplier ปรับรูปแบบข้อมูลได้

Comformist

เป็นความสัมพันธ์ระหว่าง 2 bounded context โดยให้ upstream ส่งข้อมูลให้กับ downstream แต่ผู้ที่ทำ upstream ไม่สามารถปรับรูปแบบข้อมูลที่ส่งมาให้ได้ ทาง downstream จึงต้องสร้าง anticorruption layer สำหรับรับข้อมูลทั้งหมดและปรับให้เข้ากับที่ต้องการเอง เช่น context ของการ payment เชื่อมต่อ api เข้ากับ payment gateway เป็นต้น

Open Host Service

เป็นความสัมพันธ์แบบที่มี bounded context หนึ่งสร้าง API และ document สำหรับเชื่อมต่อเข้า context เตรียมไว้แล้ว ทีมที่ทำ context อื่นสามารถเชื่อมต่อมาหา context นี้ผ่านทาง API ได้


เมื่อทำ 3 ขั้นตอนนี้แล้ว จะทำให้เห็นได้ว่าภาพรวมของระบบของเรามี bounded context ใด ควร focus ที่ context ใดและความสัมพันธ์เป็นแบบใดบ้าง พร้อมสำหรับการทำ tactical design ในขั้นตอนต่อไป

Tactical Design

Tactical design เป็นการนำระบบที่ได้จากการทำ strategic design มาทำการ design ภายในตัว Core Domain เพื่อแจกแจงการทำงานและข้อมูลทั้งหมดภายใน domain ออกมา

ขั้นตอนในการทำ Tactical Design

1. แจกแจง command, domain event และ aggregated ที่อยู่ใน Core Domain ออกมาทั้งหมด

Command คือ functionการทำงานต่างๆที่เกิดขึ้นใน domain นั้นๆ
Domain Event เป็นบันทึกที่เกิดขึ้นเหมือนมีการเปลี่ยนแปลงข้อมูลเกิดขึ้นใน domain ถ้า Command เป็นการอ่านข้อมูลอย่างเดียว จะไม่มี Domain Event เกิดขึ้น
Aggregated คือ ข้อมูลที่เกี่ยวข้องกับ Command นั้นๆ โดยมี 2 ประเภท
1. Entity — เป็นค่าจำเพาะที่เก็บใน domain โดยสามารถเปลี่ยนแปลงได้ตาม Domain Event ที่เข้ามา
2. Value object — เป็นค่าที่ดึงมาจาก entity ใน context อื่น ใช้เพื่ออ่านข้อมูลเท่านั้น
หลักในการแบ่ง Aggregated ที่ดี ควรจะ
1. 1 Aggregated ควรแทน 1 business action เท่านั้น
2. ให้ Aggregated ขนาดพอเหมาะที่สามารถใช้ข้อมูลในตัว entity เองได้
3. ให้อ้างอิง Aggregated อื่นด้วย id เท่านั้น

ตัวอย่าง

  1. Command — บันทึกข้อมูลสินค้า
    Domain Event — ข้อมูลสินค้าถูกบันทึก
    Aggregated — สินค้า
  2. Command — ดูข้อมูลสินค้า
    Domain Event — ไม่มี
    Aggregated — สินค้า

2. จัดกลุ่มของ Command ทั้งหมดตาม Aggregated ที่ใกล้เคียงกันเพื่อลด จำนวน table ที่ต้อง lock ในการทำ transaction (propagation transaction) โดยใน 1 propagation transaction ควรมีขนาดเทียบเท่า 1 aggregated เช่นกลุ่มของ Command ที่เกี่ยวกับตะกร้าสินค้า และกลุ่มของ Command ที่เกี่ยวกับคำสั่งซื้อ เป็นต้น


เมื่อจัดกลุ่มให้แบ่งแยกกันได้แล้ว แต่ละกลุ่มจะสามารถออกแบบให้ decouple ออกจากกันได้เนื่องจาก aggregated แยกออกจากกันแล้ว ถ้ามี aggregated ไหนที่ต้องเกี่ยวข้องกัน domain อื่นจะใช้วิธี subscribe Domain Event เพื่อแก้ไขตาม event ที่เกิดขึ้น

นอกจากนั้นการจัดกลุ่มจะทำให้เราสามารถเห็นภาพรวมแต่ละส่วนได้ว่ามีการอ่านเขียนข้อมูลมากน้อยเพียงใด ถ้าการใช้งานส่วนมากเป็นการอ่านข้อมูล (80% ของการใช้งานขึ้นไป) ควรใช้ CQRS ที่จะแยกคำสั่งที่มีการแก้ไขข้อมูล (Command) และคำขอเรียกข้อมูล (Query) ออกจากกัน แต่ถ้าไม่เป็นเช่นกัน ควรใช้ CRUD แทน

จากขั้นตอนทั้งหมดที่กล่าวมา เราจะสามารถเห็นภาพรวมของระบบ และความซับซ้อนในเชิงข้อมูลและความสัมพันธ์ระหว่างแต่ละ context ออกมา พร้อมสำหรับการ implementation ต่อไป

สรุป : แล้ว DDD มันดีไหม

ตามหลัก CAP thoerm ระบบที่ได้จากการทำ domain-driven design นั้นจะเน้นไปที่ Availablity กับ Partition Tolerance เพราะแต่ละส่วนจะถูกแยกกันอย่างชัดเจน ระบบยังสามารถทำงานได้แม้ส่วนอื่นจะมีปัญหา และสามารถ scale out บาง context ที่มีการใช้งานสูงได้ แต่ต้องสละ Consistency โดยจะทำเป็นแบบ eventual consistency แทน

แต่การทำ model driven development โดยใช้ domain-driven design จะเป็นการช่วยให้เห็นภาพรวมและความซับซ้อนของระบบออกมาตั้งแต่แรก ช่วยประกอบการตัดสินใจวางแผนพัฒนาระบบได้

หนังสือที่แนะนำ

ใน class คุณรุฟแนะนำหนังสือ 3 เล่มที่น่าอ่านถ้าสนใจเรื่อง domain-design design ครับ

เล่มแรก Domain-Driven Design Distilled

เนื้อหาใน workshop ส่วนใหญ่จะอ้างอิงจากในเล่มนี้

เล่มที่สอง Implementing Domain-Driven Design

เล่มนี้จะเน้นการ coding มากกว่าการ design ควรจะอ่านเล่มแรกก่อน

เล่มที่สาม Patterns, Principles, and Practices of Domain-Driven Design

Workshop Domain-Driven Design by Roofimon

ถ้าอ่าน blog แล้วยังไม่เข้าใจ ไม่ต้องแปลกใจครับ เรื่องนี้อธิบายยากมากถ้าไม่ลอง design ด้วยตัวเอง แต่ถ้าสนใจลองออกแบบระบบจริงดู ผมได้มีโอกาสลองใน class นี้ ถ้าใครสนใจลองติดตามได้ที่ Facebook ของ ODDS ครับ

อธิบายหลักการของ DDD ก่อน
อธิบายแต่ละขั้นตอนประกอบการทำ workshop
ภาพรวม design หลังจากทำ context mapping ระหว่างแต่ละ domain
ระบบหลังทำ Tactical Design โดย post-it สีเขียว,เหลือง,ม่วง เป็น Command, Domain Event และ Aggregated ตามลำดับ

ปล. ถ้ามีอะไรติชม, ข้อมูลตรงไหนผิดหรือควรแก้ไขตรงไหนสามารถ comment เข้ามาได้เลยนะครับ ขอบคุณครับ