ปัญหาที่ทีมมือใหม่มักเจอเมื่อเริ่มใช้ DDD
DDDDomain-Driven Design (DDD)แนวทางพัฒนาซอฟต์แวร์ที่เน้นสร้าง domain model ซึ่งเข้าใจกฎและกระบวนการของธุรกิจอย่างลึกซึ้ง แล้วเขียนลงใน code จริง บัญญัติโดย Eric Evans (2003) แบ่งเป็น Strategic และ Tactical DesignStrategic Design เกิดขึ้นเพื่อ “ดึงโฟกัส” ของทีมพัฒนาให้กลับมาที่ปัญหาทางธุรกิจที่ซับซ้อน ไม่ใช่กรอบงาน (framework) หรือชุด template สำเร็จรูปที่หยิบมาแปะกับ project ใดก็ได้ หัวใจของมันคือ การร่วมมือ การสำรวจ และการเรียนรู้ระหว่างฝ่ายธุรกิจกับทีมพัฒนา เพื่อผลิตซอฟต์แวร์ที่มีประโยชน์จริง
แต่ทีมที่เพิ่งรู้จัก DDD หรือยังไม่เข้าใจแก่นของมัน มักสะดุดกับกับดักชุดเดิมๆ บทนี้จะรวบรวมปัญหาที่พบบ่อยเหล่านั้น พร้อมอธิบายว่า ทำไม มันถึงเกิด และจะหลีกเลี่ยงอย่างไร — มองเป็น “รายการสิ่งที่ไม่ควรทำ” ก่อนลงสนามจริง
ให้น้ำหนักกับ tactical patterns มากเกินไป
หัวข้อที่มีชื่อว่า “ให้น้ำหนักกับ tactical patterns มากเกินไป”ลองค้นคำว่า “DDD” ใน Google คุณจะเจอแต่เรื่อง building blocks อย่าง EntityEntityอ็อบเจ็กต์ที่มี “ตัวตน” (identity) ต่อเนื่องตามเวลา แม้ค่าภายในเปลี่ยน ก็ยังเป็นสิ่งเดิม เช่น ลูกค้า, คำสั่งซื้อ — แยกแยะด้วย id ไม่ใช่ด้วยค่าTactical Design, Value ObjectValue Objectอ็อบเจ็กต์ที่สำคัญที่ “ค่า” ของมัน ไม่มีตัวตนเฉพาะ สองตัวที่ค่าเท่ากันถือว่าเท่ากัน ควรเป็น immutable (เปลี่ยนค่าไม่ได้) เช่น เงิน, วันที่, พิกัดTactical Design, AggregateAggregateกลุ่มของ Entity และ Value Object ที่ถูกมองเป็นหนึ่งหน่วยเดียวเพื่อรักษาความถูกต้องของข้อมูล มีขอบเขตชัดเจน และเป็นหน่วยของ transactionTactical Design, RepositoryRepositoryตัวที่ทำให้เราเข้าถึง Aggregate ราวกับเป็น collection ในหน่วยความจำ ซ่อนรายละเอียดฐานข้อมูล มี1 repository ต่อ1 Aggregate RootTactical Design จนหลายคนเข้าใจผิดว่านี่คือ “หัวใจ” ของ DDD แม้แต่ Eric Evans เองยังเคยบอกว่าเสียดายที่ไม่ได้วาง strategic patterns ไว้ต้นเล่ม เพราะคนส่วนใหญ่อ่านถึงแค่ส่วน building blocks แล้วก็หยุด
ปัญหาที่ใหญ่กว่านั้นซ่อนอยู่เบื้องหลัง: คนสายเทคนิคที่สนใจแต่ pattern และการเขียน code ในระบบที่ตรรกะธุรกิจซับซ้อน การ “พิมพ์ code” ไม่เคยเป็นคอขวด code เป็นเพียง ผลพลอยได้ ของการที่นักพัฒนากับ domain expertKnowledge Crunchingการบดเคี้ยวความรู้ — ศิลปะการกลั่นข้อมูลที่เกี่ยวข้องออกจาก problem domain ร่วมกับ domain expert เพื่อสร้าง model ที่มีประโยชน์ต่อ business use caseProcess นั่งจำลองปัญหาร่วมกัน — มันคือ “ปลายทาง” ของการร่วมมือและการค้นพบ ไม่ใช่จุดเริ่ม
การหยิบมาแต่ tactical pattern โดยไม่ knowledge-crunch กับธุรกิจ ไม่สร้าง Ubiquitous LanguageUbiquitous Languageภาษากลางที่ทุกคนในทีม (ทั้งนักพัฒนาและผู้เชี่ยวชาญธุรกิจ) ใช้ร่วมกัน อิงกับ domain model โดยตรง คำเดียวกันต้องหมายถึงสิ่งเดียวกัน และสะท้อนลงใน codeStrategic Design ไม่สนใจ Bounded ContextBounded Contextขอบเขตที่ชัดเจน (มักเป็นระบบย่อยหรือทีมหนึ่ง) ที่ model หนึ่งใช้ได้และมีความหมายแน่นอน ภายในขอบเขตนี้ Ubiquitous Language สอดคล้องกัน 100%Strategic Design เรียกกันว่า “DDD lite” ทำแบบนี้ไม่ผิด แต่อย่าหลอกตัวเองว่ากำลังทำ DDD เต็มรูปแบบ เพราะคุณกำลังพลาด คุณค่าส่วนใหญ่ ของมันไป
ใช้สถาปัตยกรรมเดียวกันกับทุก Bounded Context
หัวข้อที่มีชื่อว่า “ใช้สถาปัตยกรรมเดียวกันกับทุก Bounded Context”DDD ไม่ได้บังคับว่าต้องใช้ CQRSCQRSCommand Query Responsibility Segregation — แยก model สำหรับ “เขียน/แก้ไข” (command) ออกจาก model สำหรับ “อ่าน” (query) เหมาะกับ domain ซับซ้อน แต่เพิ่มความซับซ้อน ใช้อย่างระมัดระวังArchitecture, Event SourcingEvent Sourcingเก็บทุกการเปลี่ยนสถานะเป็น event ที่เปลี่ยนไม่ได้ แล้วสร้างสถานะปัจจุบันด้วยการเล่น event ซ้ำ มัก “ต้องใช้คู่กับ CQRS” (Greg Young) มี event stream ต่อ1 AggregateArchitecture, messaging หรือ ORM ตัวไหน สิ่งเดียวที่มันยืนกราน คือ ความซับซ้อนของ domain model ต้องถูกแยกออกจากความซับซ้อนทางเทคนิค สถาปัตยกรรมใดที่รองรับการแยกนี้ได้ก็ถือว่าเข้ากับ DDD
จุดสำคัญคือ สถาปัตยกรรมเป็นเรื่องของแต่ละ bounded context ไม่ใช่ของทั้ง app context ที่เรียบง่ายอาจใช้ Layered ArchitectureLayered Architectureการแยก code เป็นชั้น (UI, Application, Domain, Infrastructure) โดยรวม code ที่เกี่ยวกับ domain ไว้ชั้นเดียวและแยกออกจากชั้นอื่น เป้าหมายคือการแยกส่วน (isolation)Architecture บวก Transaction ScriptTransaction Scriptรูปแบบที่จัดตรรกะธุรกิจเป็นชุดคำสั่งตามขั้นตอน (procedural) แทนการกระจายลงในอ็อบเจ็กต์ เหมาะกับงานง่าย ๆ แต่จะรกเมื่อ domain ซับซ้อนขึ้นArchitecture ก็พอ ส่วน context ที่ตรรกะรวยและเปลี่ยนบ่อยจึงค่อยลงทุนกับ Rich Domain ModelRich Domain Modelmodel domain ที่ “รวยพฤติกรรม” — object ที่เก็บทั้งข้อมูลและ business logic ไว้ด้วยกัน เป็นคู่ตรงข้ามของ Anemic Domain Model (ที่มีแต่ getter/setter แล้วผลัก logic ไปไว้ที่อื่น) tactical patterns ของ DDD จะคุ้มค่าก็ต่อเมื่อ model “รวย” พอเช่นนี้Tactical Design
ใน Cargo Shipping นั้น context การ “วางแผนเส้นทาง” (Route SpecificationRoute SpecificationValue Object ที่ระบุ “ความต้องการของลูกค้า”: ต้นทาง ปลายทาง และกำหนดเวลาถึง — มี method isSatisfiedBy() ตรวจว่าแผนเดินทางตรงตามนี้หรือไม่Tactical Design, ItineraryItineraryValue Object ที่เป็น “แผนการเดินทางจริง” ของ Cargo ประกอบด้วยลำดับของ Leg (ช่วงการขนส่งแต่ละช่วง)Tactical Design) มีกฎซับซ้อน คุ้มที่จะทำ rich domain model เต็มที่ แต่ context “จัดการข้อมูลลูกค้า/ที่อยู่” แทบไม่มีตรรกะ — ใช้ CRUD ธรรมดาก็จบ ไม่ต้องบังคับให้ทุกที่เป็น Aggregate เหมือนกันหมด
ไล่ตามความสมบูรณ์แบบของ pattern
หัวข้อที่มีชื่อว่า “ไล่ตามความสมบูรณ์แบบของ pattern”ทีมที่ห่วงแต่ code มักยึด building block patterns ราวกับเป็น “คัมภีร์” แทนที่จะเป็น “แนวทาง” โดยไม่เข้าใจว่าเมื่อไรควรแหกกฎได้ พวกเขาทุ่มแรงไปกับการทำให้ถูกต้องตามกฎทุกประการ ทั้งที่แรงนั้นควรใช้ไปกับการเข้าใจว่า ทำไม ถึงต้องเขียนสิ่งนั้นตั้งแต่แรก
DDD คือการค้นหาว่า ต้องเขียนอะไร ทำไมต้องเขียน และควรลงแรงแค่ไหน — “อะไร” และ “ทำไม” สำคัญกว่า “อย่างไร” มาก และอย่าลืมว่า tactical patterns คือส่วนที่วิวัฒน์ไปไกลที่สุดนับจากหนังสือของ Evans ส่วน strategic ยังคงซื่อตรงกับต้นฉบับเดิม
พลาดคุณค่าที่แท้จริง: Collaboration, Communication, Context
หัวข้อที่มีชื่อว่า “พลาดคุณค่าที่แท้จริง: Collaboration, Communication, Context”ปัญหาถูกแก้ไม่ใช่แค่ใน code แต่ผ่านการร่วมมือ การสื่อสาร และการสำรวจกับ domain expert
— Scott Millett & Nick Tune
คุณค่าที่แท้จริงของ DDD อยู่ที่การสร้าง ภาษากลางเฉพาะของ context (Ubiquitous LanguageUbiquitous Languageภาษากลางที่ทุกคนในทีม (ทั้งนักพัฒนาและผู้เชี่ยวชาญธุรกิจ) ใช้ร่วมกัน อิงกับ domain model โดยตรง คำเดียวกันต้องหมายถึงสิ่งเดียวกัน และสะท้อนลงใน codeStrategic Design) ที่ทำให้นักพัฒนากับ domain expert แก้ปัญหาร่วมกันได้อย่างลื่นไหล code เป็นเพียงผลพลอยได้ เป้าหมายคือ การขจัดความกำกวมในบทสนทนา รากฐานเหล่านี้ต้องวางให้เสร็จก่อนลงมือเขียน code ทีมจึงจะมีโอกาสแก้ปัญหาได้ดีที่สุด — และนักพัฒนาไม่ควรถูกวัดที่ “ปั๊ม code ได้เร็วแค่ไหน” แต่วัดที่ “แก้ปัญหาได้ดีแค่ไหน”
ก่อ Big Ball of Mud เพราะมองข้ามความสำคัญของ context
หัวข้อที่มีชื่อว่า “ก่อ Big Ball of Mud เพราะมองข้ามความสำคัญของ context”Context, context และ context อีกที — มันคือแนวคิดพื้นฐานของ DDD ปัญหาทั้งหมดไม่อาจแก้ด้วย model เดียว เราต้องสร้างหลาย model เพื่อแก้คนละปัญหา การสร้าง model ภายในขอบเขตที่ชัดเจนคือสิ่งจำเป็นที่ทำให้ code อยู่ในสภาพจัดการได้ และไม่กลายเป็น Big Ball of MudBig Ball of Mudระบบที่ปนเปกันยุ่งเหยิงไม่มีขอบเขตชัด ให้ลากเส้นล้อมไว้แล้วยอมรับสภาพ ไม่พยายามทำ model ละเอียดในนั้น และระวังไม่ให้ลามไป context อื่นStrategic Design (BBoM) หน้าที่บอกว่า context เริ่มและจบตรงไหนเป็นของ Context MapContext Mapแผนภาพที่แสดง Bounded Context ทั้งหมดและความสัมพันธ์ระหว่างกัน ช่วยให้เห็นภาพรวมและจุดเชื่อมต่อ/แปลภาษาในระบบใหญ่Strategic Design
ถ้าทีมไม่เข้าใจ context อื่น การแก้ของพวกเขาอาจ “รั่ว” ข้ามไปกระทบ context นั้น เส้นแบ่งระหว่าง model ที่พร่าเลือนหรือไม่มีเลย มักจบลงที่การ หลอมรวม model เข้าด้วยกัน ซึ่งเร่งให้เกิด BBoM อย่างรวดเร็ว ลองคิดเหมือนการใช้ Single Responsibility Principle (SRP) ในระดับสถาปัตยกรรม — แยกแนวคิดที่ไม่เกี่ยวข้องกันออกไป เพื่อให้แต่ละ model บริสุทธิ์และโฟกัส
ระวังเมื่อ domain expert ใช้ “คำเดิม” แต่กำลังพูดในคนละ context การเห็นคำซ้ำกันทำให้หลงคิดว่านำ model กลับมาใช้ซ้ำได้ — ให้ใช้หลัก DRY ภายใน bounded context เดียว เท่านั้น ไม่ใช่ทั้งระบบ อย่ากลัวที่จะใช้ชื่อและแนวคิดเดียวกันในคนละ context
คำว่า Product ใน context “catalog” หมายถึงชื่อ รูป คำบรรยาย รีวิว แต่ใน context “คลังสินค้า” มันคือ SKU น้ำหนัก ขนาดกล่อง จำนวนคงเหลือ ถ้าฝืนยัดทั้งสองให้เป็น class Product เดียว คุณจะได้ god object ที่เป็นจุดเริ่มของ BBoM — 2 model แยกกันใน Bounded ContextBounded Contextขอบเขตที่ชัดเจน (มักเป็นระบบย่อยหรือทีมหนึ่ง) ที่ model หนึ่งใช้ได้และมีความหมายแน่นอน ภายในขอบเขตนี้ Ubiquitous Language สอดคล้องกัน 100%Strategic Design ของตัวเองดีกว่า
สร้างความกำกวมเพราะไม่ยอมสร้าง Ubiquitous Language
หัวข้อที่มีชื่อว่า “สร้างความกำกวมเพราะไม่ยอมสร้าง Ubiquitous Language”ทีมที่ไม่เห็นค่าของภาษากลางมักไปสร้าง model ด้วย “ภาษาเทคนิคของตัวเอง” พอจะขอความเห็นจาก domain expert ก็ต้องคอยแปลกลับไปมา ซึ่งกรณีดีที่สุดก็กลายเป็นคอขวด ส่วนกรณีแย่ที่สุดคือทีมไปสร้าง model รอบๆ แนวคิดที่ฝ่ายธุรกิจไม่ได้ให้ความสำคัญหรือไม่เข้าใจ
ถ้าไม่มีภาษากลางร่วมกัน คุณก็สร้าง model ร่วมกันไม่ได้
และ Ubiquitous LanguageUbiquitous Languageภาษากลางที่ทุกคนในทีม (ทั้งนักพัฒนาและผู้เชี่ยวชาญธุรกิจ) ใช้ร่วมกัน อิงกับ domain model โดยตรง คำเดียวกันต้องหมายถึงสิ่งเดียวกัน และสะท้อนลงใน codeStrategic Design ยังเป็นกุญแจในการ ค้นพบ context ด้วย เพราะ bounded context ถูกนิยามจาก “ขอบเขตที่ภาษาหนึ่งใช้ได้” เป็นหลัก model ที่สร้างขึ้นโดยไร้ context และไร้ภาษาที่ชัดเจนจะกลายเป็น BBoM อย่างรวดเร็ว เมื่อแนวคิดที่ชื่อเหมือนกันถูกจับยัดเป็นหนึ่งเดียว
ออกแบบโดยเอาเทคนิคเป็นที่ตั้ง เพราะขาดการร่วมมือ
หัวข้อที่มีชื่อว่า “ออกแบบโดยเอาเทคนิคเป็นที่ตั้ง เพราะขาดการร่วมมือ”ถ้าไม่ร่วมมือกัน แนวคิดสำคัญๆ ของ domain ที่มัก “ซ่อนอยู่โดยปริยาย” (implicit) จะถูกมองข้าม ทีมจะไปคว้าแต่ผลที่เก็บง่าย (low-hanging fruit) — เช่น “คำนาม” ของ domain — แล้วสร้าง abstraction รอบๆ ศัพท์เทคนิค การร่วมมือคือการเอาคนหลายมุมมองมาช่วยกันปั้น model ไม่มีใครผูกขาดไอเดียดีๆ และไม่มีข้อเสนอไหนโง่
BA ที่ทำหน้าที่เป็นตัวแทน domain expert จะให้ requirement และบอก input/output ได้ แต่จะ ช่วยปั้น model ให้ตอบ use case ไม่ได้ การ knowledge-crunch กับคนที่ไม่ใช่ domain expert ตัวจริง เสี่ยงเป็นการเสียแรงเปล่า
ทุ่มเวลาให้กับสิ่งที่ไม่สำคัญ
หัวข้อที่มีชื่อว่า “ทุ่มเวลาให้กับสิ่งที่ไม่สำคัญ”ทีมต้องเข้าใจก่อนว่า ทำไมถึงสร้างเอง แทนที่จะซื้อของสำเร็จรูปมาใช้ การเข้าใจวิสัยทัศน์เชิงกลยุทธ์และเหตุผลของ “build over buy” ช่วยให้ทีมรวมแรงไปถูกที่ ทรัพยากรมีจำกัด ถ้าไม่โฟกัสที่ Core DomainCore Domainsubdomain ที่สำคัญและสร้างความได้เปรียบในการแข่งขันมากที่สุด เป็นเหตุผลที่เราต้องสร้างซอฟต์แวร์เองแทนการซื้อ ควรทุ่มทีมเก่งที่สุดลงตรงนี้Strategic Design — ส่วนที่เป็นหัวใจของความสำเร็จ — แล้วเกลี่ยคนเก่งบางๆ ไปทั่วทุกส่วน นั่นคือ anti-pattern
ก้าวสำคัญของทีมคือการ ยอมรับว่าไม่ใช่ทุกส่วนของระบบจะถูกออกแบบอย่างดี และไม่ใช่ทุกส่วนจำเป็นต้องถูกออกแบบอย่างดี ถ้าไม่มีโฟกัสที่ส่วนสำคัญ คนเก่งๆ จะถูกดึงไปหมกมุ่นกับ framework JavaScript ตัวใหม่ที่ชั้น presentation แทนที่จะปั้น core domain
นอกจากทีมแล้ว domain expert เองก็ต้องเข้าใจ core domain ชัดเจน expert ที่ไม่ share วิสัยทัศน์หรือไม่รู้ว่าทำไมต้องเขียนซอฟต์แวร์นี้ จะไม่มีประสิทธิภาพในวง knowledge-crunching เพราะความสับสนหรือความเฉื่อยชา
ทำเรื่องง่ายให้กลายเป็นเรื่องยาก
หัวข้อที่มีชื่อว่า “ทำเรื่องง่ายให้กลายเป็นเรื่องยาก”การเอาเทคนิคที่ออกแบบมาจัดการ “ปัญหาซับซ้อน” ไปใช้กับ domain ที่แทบไม่มีความซับซ้อนเลย อย่างดีก็เสียแรงเปล่า อย่างแย่ก็ได้ solution ที่ซับซ้อนเกินจำเป็นจนดูแลยากเพราะชั้น abstraction เป็นกอง DDD เหมาะกับ app ที่สำคัญเชิงกลยุทธ์เท่านั้น
นักพัฒนาที่ไม่ได้สนใจการส่งมอบคุณค่า แต่สนุกกับเทคนิคล้วนๆ มักจะ “ประดิษฐ์ความซับซ้อนขึ้นมาเอง” เพราะเบื่อปัญหาธุรกิจที่ดูง่าย — แล้วทิ้งระเบิดเวลาให้คนรุ่นหลังต้องมาดูแลชั้น abstraction ที่ซ้อนกันเป็นกองนั้น
ใช้ DDD กับ domain เล็กๆ ที่ธุรกิจไม่ได้คาดหวังอะไร
หัวข้อที่มีชื่อว่า “ใช้ DDD กับ domain เล็กๆ ที่ธุรกิจไม่ได้คาดหวังอะไร”ปัญหาง่ายๆ ต้องการคำตอบง่ายๆ SubdomainSubdomainส่วนย่อยของ Domain ใหญ่ เช่น Domain e-commerce แตกเป็น subdomain การสั่งซื้อ การชำระเงิน คลังสินค้า ฯลฯ อยู่ใน “พื้นที่ปัญหา” (problem space)Strategic Design ที่ไม่ได้สร้างความได้เปรียบเชิงกลยุทธ์จะไม่ได้ประโยชน์จากหลักการ DDD ทั้งหมด นี่ไม่ได้แปลว่าให้ทำลวกๆ — มันยังต้องเร็วและดูแลได้ แต่ปัญหาที่ตรรกะน้อยก็ควรได้คำตอบที่ตรงไปตรงมา
tactical patterns และการลงแรงสร้าง UL ควรสงวนไว้ให้ core subdomain เท่านั้น ส่วน generic/supporting subdomain แค่ทำให้มันทำงานได้และแยกออกจาก core ก็พอ
ใน core domain อย่าง “ตะกร้า/การชำระเงิน” ของ e-commerce การลงทุนกับ rich model นั้นคุ้ม แต่หน้าจอ “แก้ที่อยู่จัดส่ง” ที่เป็นฟอร์มกรอกข้อมูลล้วนๆ — แค่ CRUD ก็จบ การไปสร้าง Aggregate, Repository, Domain Service ให้มันคือการ over-engineer ที่ไม่เพิ่มคุณค่าให้ธุรกิจเลย
อย่าตีตรา CRUD ว่าเป็น anti-pattern
หัวข้อที่มีชื่อว่า “อย่าตีตรา CRUD ว่าเป็น anti-pattern”ไม่ใช่ทุกส่วนของระบบที่ต้องออกแบบอย่างดี การพยายามขัดทั้ง codebase ให้เพอร์เฟกต์คือการเสียแรง สำหรับส่วนที่แทบไม่มีตรรกะ — แค่ฟอร์มบนข้อมูล — เลือก CRUD / Active Record / Transaction ScriptTransaction Scriptรูปแบบที่จัดตรรกะธุรกิจเป็นชุดคำสั่งตามขั้นตอน (procedural) แทนการกระจายลงในอ็อบเจ็กต์ เหมาะกับงานง่าย ๆ แต่จะรกเมื่อ domain ซับซ้อนขึ้นArchitecture เพื่อลดเวลาที่ใช้ แล้วเก็บเวลานั้นไว้ทุ่มให้ core domain คุณ ไม่ได้ เป็นโปรแกรมเมอร์ที่แย่เพราะไม่มี domain model
ถามตัวเองเสมอ: ความซับซ้อนที่เพิ่มมานี้คุ้มไหม?
หัวข้อที่มีชื่อว่า “ถามตัวเองเสมอ: ความซับซ้อนที่เพิ่มมานี้คุ้มไหม?”เวลา junior เพิ่งรู้จัก design pattern พวกเขามักอยากเอาไปใช้กับทุกบรรทัด พฤติกรรมเดียวกันเกิดกับคนเพิ่งรู้จัก DDD — เอา tactical pattern ไปแปะทุก project โดยไม่ดูว่าคุ้มไหม
อย่าลงทุนพัฒนาซอฟต์แวร์ที่ไม่ใช่งานเชิงกลยุทธ์ ถ้าของสำเร็จรูปเพียงพอแล้ว และถ้าต้นทุนในการทำให้กระบวนการเป็นอัตโนมัติสูงเกินคุ้ม ก็ปล่อยให้มันเป็นงานทำมือต่อไป — คำตอบไม่จำเป็นต้องเป็นเทคนิคเสมอไป
ประเมินต้นทุนของการทำ DDD ต่ำเกินไป
หัวข้อที่มีชื่อว่า “ประเมินต้นทุนของการทำ DDD ต่ำเกินไป”การทำ DDD ให้เต็มรูปแบบนั้น ยากและแพง ทั้งเวลาและคน จึงสมเหตุสมผลที่จะทุ่มเต็มที่เฉพาะส่วนสำคัญที่สุด — core domain — และมันแขวนอยู่บนเงื่อนไขว่าธุรกิจ เต็มใจ จะมาทำงานกับคุณ ไม่ใช่ปล่อยให้คุณทำคนเดียว
พยายามสำเร็จโดยไม่มีทีมที่มีแรงจูงใจและโฟกัส
หัวข้อที่มีชื่อว่า “พยายามสำเร็จโดยไม่มีทีมที่มีแรงจูงใจและโฟกัส”DDD ไม่ได้เหมาะกับทุกคนและทุก project เพื่อให้ได้ประโยชน์สูงสุดคุณต้องมี core domain ที่ซับซ้อนและจะถูกลงทุนต่อเนื่อง มีกระบวนการพัฒนาแบบ iterative และเข้าถึง domain expert ได้ นอกจากนั้นยังต้องมี:
- หลักการออกแบบที่แน่นพอจะ refactor ได้อย่างมั่นใจ
- สายตาในการออกแบบที่คม (sharp design sense)
- ทีมที่โฟกัส มีแรงจูงใจ และมีประสบการณ์
เหมือนกับการเปลี่ยนจาก waterfall มาเป็น agile/XP ไม่ได้แก้ทุกปัญหา การหันมาทำ DDD ก็ไม่ได้ผลิตซอฟต์แวร์ที่ดีขึ้นมาเองโดยอัตโนมัติ ปัจจัยร่วมของทุก project ที่สำเร็จคือ ทีมคนเก่งที่หลงใหลในงานและใส่ใจว่ามันจะสำเร็จ
พยายามร่วมมือ ทั้งที่ domain expert ไม่ได้อยู่กับ project
หัวข้อที่มีชื่อว่า “พยายามร่วมมือ ทั้งที่ domain expert ไม่ได้อยู่กับ project”การเป็นเจ้าของและการลงทุนของฝ่ายธุรกิจ โดยเฉพาะจาก domain expert คือกุญแจของการร่วมมือที่ได้ผล ถ้า expert ไม่ได้อินกับ project หรือไม่เข้าใจวิสัยทัศน์ ทีมก็ยากจะค้นพบ model ที่มีประโยชน์ ที่น่าสนใจคือ เมื่อร่วมมือกับ expert จริงๆ บางครั้งทางออกที่ดีที่สุดคือ การออกแบบกระบวนการธุรกิจ (business process) เสียใหม่เพื่อขจัดปัญหาที่ต้นตอ จนไม่ต้องเขียนซอฟต์แวร์เลยด้วยซ้ำ
เรียนรู้ในกระบวนการที่ไม่เป็น iterative
หัวข้อที่มีชื่อว่า “เรียนรู้ในกระบวนการที่ไม่เป็น iterative”DDD คือการ brainstorm คือการเรียนรู้ร่วมกัน คือการไม่หยุดที่ไอเดียแรกแต่ทดลองต่อจนเจอของที่ดีกว่า ทั้งหมดนี้ใช้เวลา กระบวนการที่ไม่รองรับการวนซ้ำก็รองรับ DDD ไม่ได้
model ที่ดีจะไม่เกิดในครั้งแรก model และภาษา “ใช้ได้เฉพาะช่วงเวลาหนึ่ง” เท่านั้น ทีมที่ไม่เข้าใจข้อนี้จะเห็นของดีที่ตัวเองสร้างค่อยๆ กลายเป็น BBoM model ต้องการความรัก ต้องถูกขัดเกลาและ refactor เมื่อเข้าใจ domain ลึกขึ้นและมี use case ใหม่มาท้าทาย
จะทดลองและวิวัฒน์ model ได้ ต้องมี unit test คอยรองรับ ไม่จำเป็นต้องทำ TDD อย่างเคร่งครัด แต่ต้องมั่นใจว่า code ยังถูกต้องหลังการ refactor หลายๆ รอบ
ใช้ DDD กับทุกปัญหา
หัวข้อที่มีชื่อว่า “ใช้ DDD กับทุกปัญหา”ทุก solution ต้องมีปัญหาที่เหมาะกับมัน DDD เป็นเครื่องมือที่ดีในกล่องเครื่องมือ แต่ถ้าธุรกิจไม่ซับซ้อนหรือไม่ค่อยเปลี่ยน ก็อย่ารีบคว้า “ค้อน DDD” มาทุบทุกอย่าง — มีเครื่องมืออื่นที่เหมาะกว่า
flowchart TD
A[bounded context นี้] --> B{ตรรกะธุรกิจ<br/>ซับซ้อน + เปลี่ยนบ่อย?}
B -- ใช่ และเป็น core --> C[Model-Driven Design<br/>Rich Domain Model + UL]
B -- ไม่ --> D[CRUD / Active Record /<br/>Transaction Script]
ยอมแลกความ pragmatic เพื่อความบริสุทธิ์ที่ไม่จำเป็น
หัวข้อที่มีชื่อว่า “ยอมแลกความ pragmatic เพื่อความบริสุทธิ์ที่ไม่จำเป็น”อย่าไล่ตามความสมบูรณ์แบบในที่ที่ไม่ต้องการมัน สำหรับ generic/supporting subdomain ให้ทำให้มันเรียบง่าย เขียนให้มันทำงานได้แล้วเดินหน้าไป core domain ต่างหากคือที่ที่คุณจะตามหาความสมบูรณ์แบบได้ — ball of mud ก้อนเล็กๆ ที่ถูกแยกออกจาก context อื่นอย่างชัดเจน บางทีก็เป็นทางเลือกที่ดีกว่า เพราะเป็น code สำหรับโยนทิ้ง (throw-away code) ที่เขียนได้เร็ว เก็บให้พ้นทาง และถ้าต้องเปลี่ยนก็เขียนทับใหม่ได้ทันที
เสียแรงไปกับการตามหา “ตราประทับรับรอง”
หัวข้อที่มีชื่อว่า “เสียแรงไปกับการตามหา “ตราประทับรับรอง””เมื่อคุณสร้างระบบตามหลัก DDD จะไม่มีใบประกาศจาก Eric Evans ส่งมาทางไปรษณีย์ชมว่าคุณทำสำเร็จ การทำตาม pattern language หรือ methodology อย่างหลับหูหลับตาโดยไม่คำนึงถึง context เฉพาะของตัวเอง คือความเขลา
DDD คือ กระบวนการเรียนรู้ การเดินทางสำคัญกว่าปลายทาง และการเดินทางนั้นคือการสำรวจ domain ร่วมกับ domain expert ไม่ใช่การนับว่าคุณยัด design pattern ลงไปได้กี่ตัว
อย่าพยายามสร้าง repository pattern ที่เพอร์เฟกต์ และอย่าไปขอคำยืนยันจากเพื่อนที่ไม่ได้อยู่ใน project เพราะถ้าไม่มี context ครบ คำแนะนำนั้นต้องรับไว้แบบมีข้อสงสัย
ไล่ล่า “code สวยงาม” ในทุกที่
หัวข้อที่มีชื่อว่า “ไล่ล่า “code สวยงาม” ในทุกที่”ทีมที่หมกมุ่นกับการแปะ design pattern โดยไม่ดูความจำเป็นจริง มักได้สถาปัตยกรรมที่ซับซ้อนเกินจนพลาดเป้าหมายของผลิตภัณฑ์ ในส่วนสำคัญที่เปลี่ยนบ่อย การมี supple design (design ที่ยืดหยุ่น) ช่วยให้ model ปรับตัวได้โดยไม่กระเพื่อมไปทั้งระบบ แต่การเค้น design ให้สง่างามในที่ที่ไม่มีคุณค่าทางธุรกิจ คือการลงแรงที่สูญเปล่า
ในตอนทำ core domain ของระบบขนส่ง ทีมควร “รอ” ก่อนผูกมัดกับ pattern ใด pattern หนึ่ง การชะลอการ refactor แล้วอยู่กับ code เพื่อดูว่าตรงไหนเสียดสีหรือเปลี่ยนบ่อยที่สุด จะเผยความจริงของ domain ออกมา และนำไปสู่การเลือก design ที่มีข้อมูลรองรับมากกว่า
อย่าให้ design pattern และหลักการมาขวางการส่งมอบคุณค่า DDD คือการมอบคุณค่า ไม่ใช่การผลิต code สง่างาม — pattern เป็นเพียงรายละเอียดการ implement เป้าหมายจริงคือเข้าใจ domain ให้ลึกพอจะแก้ปัญหาในนั้นได้
สรุปประเด็นสำคัญ
หัวข้อที่มีชื่อว่า “สรุปประเด็นสำคัญ”- tactical patterns ช่วยสร้าง domain model ที่ดีได้ แต่เป็นส่วนที่ถูกให้น้ำหนักเกินจริง — คุณค่าของ DDD ไม่ได้อยู่ตรงนี้
- DDD เป็นมากกว่าการเขียน code: การร่วมมือกับ domain expert เพื่อ knowledge-crunch และมีความเข้าใจร่วมที่แสดงออกผ่าน Ubiquitous Language คือเสาหลัก
- Context คือทุกสิ่ง — การแบ่ง context และแยกส่วนช่วยรักษาความถูกต้องของ code ลดภาระทางความคิด (cognitive load) และทำให้แต่ละ model คมชัดและโฟกัส
- คุณต้องมีทีมเก่งที่ทุ่มเทและเต็มใจเรียนรู้ domain และต้องเข้าถึง domain expert ได้
- ใช้ CRUD กับ bounded context ที่ความซับซ้อนต่ำได้เลย — ไม่มี domain model ไม่ได้แปลว่าคุณเป็นโปรแกรมเมอร์ที่แย่
- Bounded Context และ Ubiquitous Language คือรากฐานของ DDD และ DDD คือกระบวนการของการเรียนรู้ ขัดเกลา ทดลอง และสำรวจ เพื่อค้นพบ model ที่มีประโยชน์ร่วมกัน
เจาะลึกแนวคิดในบทนี้ต่อได้ที่คลังอ้างอิง DevIQ:
- Tactical Design — ทำความเข้าใจว่า tactical patterns คืออะไรและควรอยู่ตรงไหนในภาพรวม ก่อนจะให้น้ำหนักกับมันมากเกินไปแบบที่บทนี้เตือน
- Strategic Design — ส่วนที่หนังสือย้ำว่าสำคัญกว่า tactical แต่ทีมมือใหม่มักมองข้าม
- Bounded Context — แก่นของปัญหา “Big Ball of Mud” ที่เกิดจากการมองข้ามขอบเขต context ตามที่บทนี้อธิบาย
- Ubiquitous Language — ภาษากลางที่ขาดไม่ได้สำหรับการร่วมมือกับ domain expert ซึ่งเป็นคุณค่าแท้จริงของ DDD ตามที่บทนี้เน้นย้ำ
เช็กความเข้าใจ — บทที่ 9
ข้อ 1 / 4ตามหนังสือ ทำไมการให้น้ำหนักกับ tactical patterns มากเกินไปจึงเป็นปัญหา?