Domain Driven Design (DDD) สไตล์ แมวๆ จากพี่โจสตาร์ #LearningWithSCK
เหตุการณ์ส่งมอบความรู้ที่ SCK Dojo วันที่ 17 มีนาคม 2566 เวลาประมาณ 3 ชั่วโมง
Domain Driven Design
เกิดมาในช่วงปี 2005 โดย Eric Evans ซึ่งช่วงนั้นยัง Based on สิ่งที่เรียกว่า OOP ซึ่งจะคาบเกี่ยวกับ Agile (ที่มีมาช่วงปี 2000) เพราะฉะนั้นกลิ่นอายของ DDD จะมี Iterative and Incremental รวมอยู่ด้วย
- Incremental จะมีสิ่งที่เรียกว่า Supple Design ความยืดหยุ่น ปรับตัวได้ง่าย ในการ เพิ่ม/ลด/ปรับ/แก้ Feature
- Iterative ก็ใช้คำว่า Supple Design เช่นกัน ทำงานกันเป็นรอบๆ
- และจะได้ยินคำว่า Effective Design คือ เราไม่จำเป็นต้องทำ Perfect Design แต่ทำให้มัน “พอใช้งานได้” และ “ตอบโจทย์ Business ในช่วงเวลานั้น”
แล้วค่อยเพิ่มไปเรื่อยๆ Incremental ไปตามการเปลี่ยนแปลงของ Business
Domain
ถ้าเปิด Dictionary จะหมายถึง ขอบเขต อาณาเขต อาณาจักร ความสนใจ พื้นที่ควบคุม
ในหนังสือของ Eric Evans จะเขียนคำว่า sphere คือกลุ่มก้อนของความรู้ ที่มี Business, Process, Rule ของกรอบความรู้ในเรื่องๆ หนึ่ง
ซึ่งต้องคุยกับ Domain Expert
ทุกคนสามารถเป็น Domain Expert ได้ BA, SA, Users ก็เป็นได้, Specialist ในกระบวนการผลิตอะไรบางอย่างก็เป็นได้ เช่น พี่โจมีความรู้เรื่องกาแฟ ก็เป็น Domain Expert ด้านกาแฟ
เมื่อเรามี Domain Expert แล้ว แต่จะ Capture สิ่งที่อยู่ในหัวเค้าออกมาได้ยังไง?
ก็จะมีตัวละครอีกตัวโผล่มาคือ Domain Modeler ทำหน้าที่ออกโมเดลบางอย่าง เพื่อแก้ปัญหาบางส่ิง
- ถ้าวง UX ก็คือ Users -> Modeler = UXers
- ถ้าวง Software ก็คือ Users, BA, … -> Modeler = Software Architecture, Solution Architecture
ยกตัวอย่างเช่น การทำระบบสูตรสินค้า Domain Expert ในที่นี้คือ Product owner, ทีม R&D
Model Driven Design
Model
(ขอบคุณพี่บอม แขกรับเชิญ ช่วยอธิบายให้เห็นภาพมากขึ้น)
เมื่อก่อน เราเดินไปคุยกับลูกค้า เราเขียนโปรแกรม ยาวๆ จากบนลงล่าง เวลาลูกค้าขอแก้ ก็ต้องมานั่งไล่สิ่งที่ยาวๆ นั้น ยาก และเวลาจะอธิบายว่า สิ่งที่เราเขียน กับ สิ่งที่ลูกค้าต้องการเป็นเรื่องเดียวกัน ก็ยาก (Structural, Procedure Programming)
แต่ปัญหาไม่ได้อยู่ตรงนั้นหรอก มันเขียนเป็น Function เขียนเป็นส่วนๆ และทำให้อ่านง่ายได้ ปัญหาอยู่ที่การออกแบบ
เราจะเอาสิ่งที่อยู่ในหัวลูกค้า ออกมาทำเป็น Software แล้วทำให้ทั้งสองอย่างมันเหมือนกันได้อย่างไร
- เวลาลูกค้าพูดถึงสิ่งนี้ เรารู้ทันทีว่า จะต้องไปเขียน/เอาโค้ดจากตรงไหน
- ก็เลยเกิดมาเป็น OOP (object oriented programming)
- ของทุกสิ่งเป็น Object มีคุณลักษณะ ที่ไม่เหมือนกัน
- ถ้าจะเขียน Software ผลิตรถยนต์ — อะไหล่เป็น Object แต่ละ Object ก็มี attribute ของตัวเอง
- แต่ OOP คือ low level มันต้องไปแกะ object ต้องไปดูว่าในโรงงานมีอะไรให้เราแกะออกมาบ้าง เพื่อทำเป็น software
- คนก็เป็น object สิ่งของก็เป็น object กระบวนการก็แปลงไปเป็น object ได้อีก
ยุ่บยั่บและความสัมพันธ์ที่ยุ่งเหยิง
- พอความรู้มันใหญ่มาก อะไรที่ลงรายละเอียดมาก มันเริ่มกินยาก ทำความเข้าใจยาก การไปดูโค้ดที่เขียนเพื่อใช้ในโรงงาน ก็ไม่รู้ต้องเริ่มจากตรงไหน
- ก็เลยเกิดเป็นสิ่งใหม่คือ การผ่องถ่ายความรู้บางอย่างออกมาเป็น Model
ถ้าอยากรู้ว่า รถนี้สวยไหม จะขายได้ไหม?
- ก็วาดออกมา เป็นรูปซักรูปหนึ่ง เพื่อให้ใช้ในการสื่อสารได้
- ถ้าต้องวาดหลายๆ ด้าน แล้วมันเหนื่อย ก็เริ่มทำเป็น โมเดลสามมิติหมุนได้ในเครื่องคอม
- ยังไม่พอ ก็พิมพ์มันออกมา ให้มันจับต้องได้ ให้มันเหมือนจริงที่สุด
โมเดล คือ การทำให้เราเข้าใจตรงกันว่า สิ่งที่เราคิด สิ่งที่เค้าคิด สิ่งที่เราทำ มันคือเรื่องเดียวกัน
- คือ การทำให้ level ของความเข้าใจมันกว้างขึ้นหน่อย เห็นภาพรวมมากขึ้น
- โมเดลแบบไหนที่จะสามารถเห็นภาพรวมได้ เห็นแต่ละส่วนได้ (แต่ไม่ต้องเห็นชิ้นส่วน) เช่น ส่วนนี้เป็นส่วนงานขาย งานผลิต การจัดการงานในองค์กร
เรื่องราวของภาษา
เวลาเข้าไปในแต่ละโปรเจ็กต์ แต่ละบริษัท แต่ละสถานที่ ก็จะมีภาษาของตัวเอง
เช่น วงการ Loan ลูกค้าบอกต้องทำแอป เพื่อขอสินเชื่อรถยนต์
ก็ต้องคุยกับ Domain Expert ในวงการนั้นว่า “แอป” ของคุณคืออะไร
- ถ้านักพัฒนา Software อย่างเราเข้าใจ คือ Mobile Application
- ซึ่ง User อาจจะหมายถึง ใบสมัครขอสินเชื่อรถยนต์ที่เป็นกระดาษ
- หรือ “แอ๊บ” ที่อาจจะหมายถึงการทำตัวน่ารัก (อันนี้ไม่เกี่ยว)
Ambiguous คือ คำที่มีความหมายคลุมเครือ เป็นสิ่งที่คนทำ Model จะต้องเข้าใจ
Ubiquitous language คือภาษาถิ่น ภาษาใน System, Industry, Domain นั้นเท่านั้น คำว่า “ฉัน” ในแต่ละท้องที่ ก็ไม่เหมือนกัน
พอเราได้ภาษาของลูกค้ามาแล้ว ก็คุยในภาษาเดียวกัน
Driven คือการขับเคลื่อน ก่อให้เกิดโมเดลที่เราสนใจขึ้นมา
- การตี Scope และค่อยๆ เจาะลงไป ทำให้เราโฟกัสได้
- level แรกคือทำให้เห็นภาพใหญ่ก่อน
- อยากรู้รายละเอียดต่อ แต่เราไม่สามารถทำพร้อมๆ กันได้
- พอบริบทของเรื่องนั้นมันใหญ่ เราคิดตามได้ไม่หมด ก็ค่อยๆ แบ่ง และลงรายละเอียดเฉพาะบางเรื่อง เพื่อเจาะหาข้อมูลเพิ่มเติม
Bounded context
คือการคิดว่า เอาอันนี้มาเป็น Module ได้ไหมนะ?
- มันต้องหาขอบเขตให้ได้ ถ้าหาไม่ได้ มันจะทะลัก ความรู้มันจะเยอะ จนเราไม่สามารถ Capture มันทัน (อาจจะเป็นความรู้สึกของฉันตอนนี้ที่ตาลาย สมองเบลอ 555+)
- ถ้าใครเลี้ยงหมา ให้คิดถึง หมาที่ขนเป็นสังกะตัง อยากแก้โค้ดแค่นิดเดียว แต่ต้องไปสางขนจำนวนมากก่อน
ทุกอย่างคือแผนที่ (Map)
- ซึ่งจะเกิดแผนที่ไม่ได้เลย ถ้าเรายังเรียกชื่อไม่ตรงกัน
- ถ้าได้คำพูดที่ชัดเจนเพียงพอแล้ว เราจะเริ่ม bounded ได้ แบ่งแยกได้ว่าอะไรอยู่ตรงไหน
- จากก้อนใหญ่ แบ่งเป็นก้อนย่อย แล้วตั้งชื่อให้แต่ละ Module แบ่งลงไปอีก เพื่อให้โฟกัสบางส่วนได้
- พอแบ่งได้ จะเกิดขั้นตอนที่เรียกว่า การระบุความสำคัญ (Prioritize) ในหนังสือจะเรียกว่า Sub-Domain
การดำเนินธุรกิจมีหลายโดเมน มีทั้ง Core และ Supporting
- ระบุให้ได้ว่าอะไรคือ Core Domain ของธุรกิจ เช่น Core Domain ของ BTS คือ การเดินรถไฟฟ้า
- ถ้าระบุไม่ได้ ให้มองหาว่า อะไรคือจุดที่สร้างความแตกต่าง ที่เค้ายอมลงทุนเพื่อให้เกิดการแข่งขัน ถ้าแข่งขันแล้วชนะเป็นที่หนึ่งได้ สิ่งนั้นคือ Core Domain ซึ่งอาจจะแอบหาได้จากพันธกิจหลักขององค์กร
Core ว่าสำคัญแล้ว แต่ถ้าไม่มี Supporting ก็ไม่สามารถดำเนินธุรกิจได้เช่นกัน ซึ่งแยกออกมาเป็นสองอัน
- Specific มีเฉพาะ Business นั้น เช่น การดูแลรักษารถไฟฟ้า การดูแลรักษารางรถไฟฟ้า (การดูแลรถสามารถ เป็น Generic ได้ ถ้าเราเปรียบเทียบในบริบทเดียวกัน)
- Generic เช่น ดูแลพนักงาน การเงิน การบัญชี
ถ้ามัน Generic มากๆ ก็อาจจะหา outsource หรือ open source มาช่วยได้ แต่ Core domain เป็นอะไรที่ควรทำเอง
ยกตัวอย่างให้เห็นมากขึ้น (จากเราเองที่ถามพี่โจ) เช่น พร้อมเพย์ อาจจะเป็น Core domain ของธนาคารกลาง แต่เป็น generic supportive ของธนาคารอื่นๆ ที่ต้องมีและมาใช้
การรู้ว่าอะไรเป็น Core ช่วยเรื่อง prioritization
สิ่งสำคัญของ DDD คือ Strategic Design การดีไซน์เชิงกลยุทธ์
- การทำ Domain Driven, Model Driven คือการทำแผนที่ของ Software
- จริงๆ มันมีอยู่นานแล้ว แต่มันเพิ่งมาดัง เพราะมีคนบอกว่า Microservice สามารถใช้ Domain Driven Design ช่วยออกแบบได้
Tactical Design
เป็นการนำระบบที่ได้จากการทำ strategic design มาทำการ design ภายในตัว Core Domain เพื่อแจกแจงการทำงานและข้อมูลทั้งหมดภายใน domain ออกมา (ซึ่งจะมี keyword อีกมากมายโผล่ออกมา วันนี้จะยังไม่เรียน ไม่งั้นเราอาจจะสมองไหม้มากกว่านี้)
พอเราแบ่ง Context แล้ว Map ความสัมพันธ์ มันจะมีท่าอยู่ประมาณ 3 ท่า
- Anti-Corruption Layer (ACL) is a set of patterns placed between the domain model and other bounded contexts or third-party dependencies. The intent of this layer is to prevent the intrusion of foreign concepts and models into the domain model.
เราจะทำ Adapter คือ ไม่ว่าใครจะส่งอะไรมาเท่าไหร่ก็ไม่สน จะรับเท่าที่อยากรับ (ยกตัวอย่างหนังสือ “กล้าที่จะถูกเกลียด”) - Upstream (Sender, Producer) and Downstream (Receiver, Consumer)
เรามี system A และ B
ถ้ามีการ update ที่ A เช่น เงินเดือนเข้า และ A ต้องไปบอก B คือภรรยาว่าเงินเข้าแล้ว X บาท B ก็มีหน้าที่รับและไป Process อะไรนู่นนี่นั่น
A = Upstream
B = Downstream
ถ้าเกิด B ไปเช็คแล้วพบว่า เอ้า A ให้เงินมาไม่ครบ ต้องโดนจิกหัว
B = Upstream
A = Downstream
พอเรากำหนดความสัมพันธ์ได้แล้ว ก็ค่อยมาตกลงว่าคุยกันยังไง เช่น RESTful หรือ .csv file แบบ Daily (ทำเป็น job) - Subscription
เวลาออกแบบ Application ปัญหาปัจจุบันที่เจอคือ Dev มักตั้งต้นด้วย ER-Driven (การออกแบบ DB ให้ดี เพราะเมื่อก่อน Storage แพง และพวกเราก็ร่ำเรียนมาแบบนั้น) แล้วจะไปเจอข้อจำกัดของ Relational DB (RDBMS) => ACID: Atomicity, Consistency, Isolation, Durability
RDBMS ก็เลยมี Normalization เพื่อเข้ามาแก้ ยิ่งทำก็ยิ่งแตกกิ่ง แตก DB ไปเรื่อยๆ แล้ว Solution ก็ไปต่อไม่ได้ ก่อให้เกิดปัญหา Performance
(พี่บอมกลับมาเสริมอีกครั้ง เพื่ออธิบาย Normalization เพราะสภาพคนเรียนอย่างฉันน่าจะตาลอยไปชั้น 10 แล้ว)
ยกตัวอย่างเช่น คำนำหน้าชื่อ มี นาย นาง นางสาว เด็กชาย เด็กหญิง ฯลฯ ถ้าเฉลี่ยคือ 5 Chars ถ้าเรามี 500k records ก็จะปาไป 2.5M ตัวอักษร
- ในเมื่อมันเหมือนๆ กัน ก็เอา นาย = 1, นาง = 2, … ลด Character ลดจำนวนการเก็บได้อีก
- ถ้าจะเปลี่ยน นาย เป็น นายบ่าว ต้องไปนั่งไล่ replace เปลี่ยนเหนื่อยตาย
- การทำแบบนี้ เปลี่ยนแค่จุดเดียว คือเปลี่ยน นายบ่าว = 1
- พออยากได้รายชื่อออกมาเป็นตาราง ก็ต้อง Query ออกมา ถ้าเจอ 1 ให้เป็น นายบ่าว มาแทน ก็ไม่ยาก แต่มันเปลืองตอนอ่าน
- ก็เลยเกิดแนวคิดของภาษาหนึ่งขึ้นมา SQL (Structured Query Language) คือมันมี Add, Insert, ฯลฯ แต่ก็ถูกใช้น้อย ส่วนใหญ่ที่ใช้คือ Query ข้อมูลออกมา เพื่อการอ่านข้อมูลที่เหมาะสม
- การทำ Table เดียว กลายเป็น 2 Table คือการทำ Normalization แตกข้อมูลจาก 1 Table ออกเป็นหลายๆ ที่ เพื่อให้ทำการจัดเก็บง่ายขึ้น
- ซึ่งมีการทำได้มากสุดถึง 5 Level Normalization!
แต่มันก็ยังขาดเรื่อง System Context ว่าเราต้องการแก้ปัญหาอะไร
C4 Model
visualizing software architecture
หากเราลองพิมพ์ software architecture diagram ใน Google จะเจอภาพที่เราดูแล้วก็เอ๊ะ (แต่ก็เห็นบ่อยๆ ในการทำงาน) มันเอามาแก้ปัญหาอะไรนะ?
เพื่อให้เห็นภาพการทำงานของ C4 มากขึ้น
ยกตัวอย่างเช่น โลก — ประเทศ — จังหวัด — อำเภอ — ตำบล — หมู่
Software ก็เหมือนกัน เริ่มจาก Context — Containers — Components — Code
- Context คือ ประเทศไทย
คือ System หนึ่งที่เราสนใจอยู่
เริ่มจากการ Set ภาพนี้ให้ตรงกันก่อน ทั้ง Business และ Software แล้วค่อยไปซูมลงไปในแต่ละเรื่อง - Containers คือ จังหวัด จังหวัดอะไรที่สำคัญกับการติดต่อข้างนอก ก็คือเมืองหลวง แปลว่ากรุงเทพ = Core Domain
ในกรุงเทพมีกี่เขต ก็คือการแบ่ง Boundary
เมื่อเราลงรายละเอียด จะเริ่มเห็นว่า จังหวัดของเรา มีขึ้นตรงกับประเทศไหนข้างนอกอีกไหม
Containers ใน System Boundary คือ Context ใหญ่ มี Context ย่อยอะไรบ้าง พอเราซูมเข้าไป จะเริ่มเห็นของข้างในเพิ่ม อาจจะเป็นการแบ่งเรื่อง Search, Shopping Cart, Payment
พอแบ่งได้ ก็จะเริ่มโฟกัสได้ - Components ซูมเข้าไปอีก จะเริ่มเห็นของข้างในมากขึ้นอีก
- Code
ในหนังสือจะเริ่มจากชั้น Domain Model
- แล้วค่อยทำขึ้นบน เพื่อออกแบบ Business Logic, Web API, Web
- หรือลงล่าง Infrastructure, DB
C4 (การทำ Map) ก็เริ่มจาก
- User คือใคร Define Personas, Set System Context
- ระบบนี้ต้องไป call service อะไรบ้าง เช่น SAP, Email, …
- พอ Zoom เข้าไปที่ Level2 (Containers) ถ้า map กับ DDD คือ bounded context และ มีเรื่องของ layer architecture เข้ามาเกี่ยวข้อง ก็คือเจาะเข้าไปในกล่อง SAP หรือ กล่อง Email อีก
- พอ Zoom เข้าไปที่ Level3 (Components) ซึ่งมีหลายท่า อาจจะใช้ท่าแนวตรง เช่น Material, Formulation (น่าจะอารมณ์เป็นรายฟีเจอร์ไหมนะ?)
จะมีอีกคอนเซปท์คือ evolutionary architecture (incremental design) ค่อยๆ เปลี่ยนตามความเหมาะสม
(เมื่อโลกยังไม่หยุดหมุน ความต้องการคนไม่เคยหยุด ยังไงงานที่ทำก็ต้องถูกปรับเปลี่ยน ทำเพิ่มอยู่แล้ว อย่ามาพูดคำว่า Rework นะ หึหึ)
ถ้าเราวางโครง Model ไว้ดี เวลามีปรับอะไร มันก็เห็นได้ชัดว่าต้องปรับตรงไหน ถ้าปรับตรงนี้จะกระทบส่วนใด แล้ววันดีคืนดี คนอื่นอยากใช้บ้าง ก็ดันมันขึ้นเป็น Service ได้ สิ่งนี้เรียกว่า Supple Design
- ขอบคุณผู้สอนหลัก พี่โจสตาร์
- ผู้ช่วยขยายความให้เห็นภาพ พี่บอม
- ภาพประกอบจากการถ่ายรัวๆ น้องบี
ถ้าให้ Reflect ความรู้ที่ได้รับวันนี้ จากคนที่ไม่รู้จักเลยว่า DDD คืออะไร ก็ถือว่าได้ Overview มานิดนึง อาการตาลอย สมองเบลอมีเยอะมาก เมื่อเทียบกับการเรียนรู้เรื่องอื่นๆ ได้รู้จัก C4 ใหม่ แน่นอนว่าก็ได้แค่ Concept เช่นกัน ไม่สามารถลงมือทำเองได้แน่นอน 5555
ชอบ Concept ของการ Supple (เรานึกถึงคำว่า Flexibility) ในเชิง Software Architecture แบบนี้ มันคือการทำงานร่วมกันของทั้งทีม ในแนวทางการทำงานแบบ Agile Software Development
ทำให้ดีเพียงพอที่สุดกับความต้องการเวลานี้ (และพร้อมรับการเปลี่ยนแปลง!)
ถ้ารากฐานถูกวางมาอย่างดีและมั่นคง การต่อเติมโครงสร้าง (ที่ไม่เวอร์เกินไป) ก็ไม่ทำให้บ้านพังทลาย ไม่ต้องทำของใหม่มา Replace ระบบกันบ่อยๆ
แบบที่พี่บอมเคยบอกไว้ ณ เชียงใหม่ ว่า “ถ้าการสร้างซอฟต์แวร์ไม่ได้แก้ปัญหา แปลว่าเรากำลังสร้างปัญหาใหม่ สัตว์ประหลาดตัวใหม่”