ข้าม​ไป​ยัง​เนื้อหา

Strategic Design ด้วย Context Mapping

Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design แต่ละ​อัน​ไม่​ได้​อยู่​อย่าง​โดดเดี่ยว เกือบ​ทุก​ระบบ​จริง​ต้อง​เชื่อม​ต่อ​กับ context อื่น — ทั้ง​ของ​ทีม​เรา​เอง​และ​ของ​ระบบ​ภายนอก เมื่อ2 model มา​บรรจบ​กัน คำถาม​สำคัญ​จึง​ไม่ใช่​แค่ “เชื่อม​ด้วย​เทคโนโลยี​อะไร” แต่​คือ “ความ​สัมพันธ์​ระหว่าง​ทีม​ที่​ดูแล2 context นี้​เป็น​แบบ​ไหน” นี่​คือ​หัวใจ​ของ Context MappingContext Mappingกิจกรรมการ​ระบุ Bounded Context และ​นิยาม​ความ​สัมพันธ์​ระหว่าง​กัน (เช่น Partnership, Shared Kernel, ACL) เพื่อ​จัดการ​การ​เชื่อม​ต่อ​ระหว่าง​ทีม/ระบบStrategic Design

Context MapContext Mapแผนภาพ​ที่​แสดง Bounded Context ทั้งหมด​และ​ความ​สัมพันธ์​ระหว่าง​กัน ช่วย​ให้​เห็น​ภาพ​รวม​และ​จุด​เชื่อม​ต่อ/แปล​ภาษา​ใน​ระบบ​ใหญ่Strategic Design คือ​แผนภาพ​ที่​แสดง Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design ทั้งหลาย​และ​เส้น​ที่​ลาก​เชื่อม​ระหว่าง​กัน เส้น​แต่ละ​เส้น​ไม่​ได้​บอก​แค่ “กลไก​ทาง​เทคนิค” ที่​อยู่​ตรง​กลาง แต่​ยัง นิยาม​ความ​สัมพันธ์​ระหว่าง​ทีม ที่​เป็น​เจ้าของ​แต่ละ context ด้วย การ​เข้าใจความ​สัมพันธ์​นี้สำคัญพอๆ กับ​การ​เขียน code เชื่อม​ต่อ เพราะ​มัน​กำหนด​ว่า​ฝ่าย​ไหน​มี​อำนาจ​ต่อรอง ฝ่าย​ไหน​ต้อง​ปรับ​ตัว และ​ความ​ล้มเหลว​ของ​ฝ่าย​หนึ่ง​จะ​ลาม​ไป​กระทบ​อีก​ฝ่าย​แค่​ไหน

ใน​เมื่อ​สอง Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design ต่าง​มี Ubiquitous LanguageUbiquitous Languageภาษา​กลาง​ที่​ทุก​คนใน​ทีม (ทั้ง​นัก​พัฒนา​และ​ผู้เชี่ยวชาญ​ธุรกิจ) ใช้​ร่วม​กัน อิง​กับ domain model โดยตรง คำ​เดียวกัน​ต้อง​หมาย​ถึง​สิ่ง​เดียวกัน และ​สะท้อน​ลง​ใน codeStrategic Design ของ​ตัวเอง เส้น​ที่​ลาก​เชื่อม​นี้​จึง​แทน การ​แปล​ภาษา ที่​เกิด​ขึ้น​ระหว่าง​สอง​ภาษา​นั้น ลอง​นึกถึง​ภาพ​ว่า มี​สอง​ทีม​ที่​ต้อง​ทำงาน​ร่วม​กัน แต่​ทั้ง​คู่​อยู่​กัน​คนละ​ฝั่ง​ของ​พรมแดน​ประเทศ​และ​พูด​กัน​คนละ​ภาษา ทาง​เลือก​มี​อยู่​สอง​ทาง — ไม่​ทีม​ใด​ทีม​หนึ่ง​ต้อง​จ้าง ล่าม มา​ช่วย หรือ​ทีม​หนึ่ง (หรือ​ทั้ง​สอง​ทีม) ต้อง​ลงแรง​เรียนรู้​ภาษา​ของ​อีก​ฝ่าย​ให้​มาก​พอ การ​จ้าง​ล่าม​ถือว่า​เป็น​งาน​ที่​เบา​กว่า​สำหรับ​ทั้ง​สอง​ทีม แต่​ก็​อาจ​มี “ราคา” ที่​ต้อง​จ่าย​ใน​หลาย​แง่ ลอง​นึกถึง​เวลา​ที่​เสีย​ไป​กับ​การ​ที่​ทีม​หนึ่ง​ต้อง​พูด​กับ​ล่าม​ก่อน แล้ว​ล่าม​จึง​ค่อย​ถ่ายทอด​ต่อ​ไป​ยัง​อีก​ทีม (ทีม → ล่าม → อีก​ทีม) ตอน​แรกๆ ก็​ดู​ราบรื่น​ดี แต่​นาน​วัน​เข้า​กลับ​ยุ่งยาก​ขึ้น​ต่อ​เนื่อง ถึง​อย่าง​นั้น​มัน​ก็​ยัง​อาจ​ดี​กว่า​การ​ต้อง​คอย​สลับ​ไป​มาระหว่าง​ภาษา​ตลอด​เวลา — และ​ข้อ​แลกเปลี่ยน (trade-off) แบบ​เดียวกัน​นี้​เอง​ก็​เกิด​ขึ้น​เมื่อ​เรา​ต้อง​แปล Ubiquitous LanguageUbiquitous Languageภาษา​กลาง​ที่​ทุก​คนใน​ทีม (ทั้ง​นัก​พัฒนา​และ​ผู้เชี่ยวชาญ​ธุรกิจ) ใช้​ร่วม​กัน อิง​กับ domain model โดยตรง คำ​เดียวกัน​ต้อง​หมาย​ถึง​สิ่ง​เดียวกัน และ​สะท้อน​ลง​ใน codeStrategic Design หนึ่ง​ให้​กลาย​เป็น​อีก​ภาษา​หนึ่ง

ทิศทาง​ของ​อิทธิพล: ต้นน้ำ vs ปลาย​น้ำ

แนวคิด​หนึ่ง​ที่​จะ​เจอ​ซ้ำ​ตลอด​บท​นี้​คือ ความ​สัมพันธ์​แบบ​ต้นน้ำ/ปลาย​น้ำUpstream / Downstreamความ​สัมพันธ์​ที่​การกระทำ​ของ​ฝ่าย​ต้นน้ำ (upstream) ส่ง​ผล​ต่อ​ฝ่าย​ปลาย​น้ำ (downstream) แต่​ไม่ใช่​ใน​ทาง​กลับ​กัน — ทิศ​ของ​อิทธิพลStrategic Design — ฝ่าย ต้นน้ำ (Upstream) คือ​ฝ่าย​ที่​ส่ง​อิทธิพล​ออก​ไป ส่วน ปลาย​น้ำ (Downstream) คือ​ฝ่าย​ที่​ได้​รับ​ผลกระทบ บน​แผนภาพ​มัก​เขียน​กำกับ​ด้วย​ตัว​อักษร U (upstream) และ D (downstream) ตรง​ปลาย​เส้น​เชื่อม


Vernon ไล่​เรียง​รูปแบบ​ความ​สัมพันธ์​เก้า​แบบ​ที่​พบ​ได้​บน Context Map แต่ละ​แบบ​บอก​ทั้ง​โครงสร้าง​ทีม​และ​กลยุทธ์​การ​เชื่อม​ต่อ​ที่​เหมาะสม

PartnershipPartnershipสอง​ทีม​ที่​หาก​ฝ่าย​ใด​ล้มเหลว​อีก​ฝ่าย​ก็​ส่ง​งาน​ไม่​ได้ จึง​ต้อง​วางแผน​และ​จัดการ​การ​เชื่อม​ต่อ​ร่วม​กัน feature ที่​พึ่งพา​กัน​ถูก​จัด​ให้​เสร็จ​ใน release เดียวกันStrategic Design เกิด​ขึ้น​เมื่อ​สอง​ทีม​ที่​ดูแล​คนละ Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design มี​ชะตา​ผูก​กัน — ถ้า​ฝ่าย​หนึ่ง​ล้มเหลว อีก​ฝ่าย​ก็​ส่ง​งาน​ไม่​ได้​เช่น​กัน ใน​กรณี​นี้​ทั้ง​สอง​ทีม​ต้อง​ร่วมมือ​กัน​อย่าง​ใกล้​ชิด ทั้ง​วางแผนการ​พัฒนา​และ​จัดการ​การ​เชื่อม​ต่อ​ร่วม​กัน feature ที่​พึ่งพา​กัน​ต้อง​ถูกวาง​ให้​เสร็จ​ใน release เดียวกัน เพราะ​ความ​สัมพันธ์​แบบ​นี้​ดูแล​ยาก จึง​ควร​ประคอง​ไว้​เท่า​ที่​จำเป็น และ​พร้อม​ยุติ​เมื่อ​เหตุผล​ที่​ผูกพัน​กัน​หมด​ไป

Shared KernelShared Kernelสอง​ทีม​ตกลง​แบ่ง model ส่วน​เล็ก ๆ มา​ใช้​ร่วม​กัน​โดย​มี​ขอบเขต​ชัดเจน ส่วน​นี้​ห้าม​แก้​โดย​ไม่​ปรึกษา​กัน เพราะ​กระทบ​ทั้ง​สอง​ฝ่ายStrategic Design คือ​กรณี​ที่​สอง​ทีม (หรือ​มากกว่า) ตกลง​แบ่ง model ส่วน​เล็กๆ มา ใช้​ร่วม​กัน โดย​ขีด​ขอบเขต​ให้​ชัดเจน (บน​แผนภาพ​แสดง​ด้วย​ส่วน​ที่2 context ซ้อน​ทับ​กัน) ทั้ง​สอง​ทีม​ต้อง ตกลง​กัน​ให้​ชัด​ว่า​จะ​แบ่ง element ใด​ของ model มา​ใช้​ร่วม และ​เป็น​ไป​ได้​ว่า​จะ​มี​เพียง​ทีม​เดียว​ที่​เป็น​ผู้​ดูแล code, build และ test ของ​ส่วน​ที่​ใช้​ร่วม​กัน​นั้น Shared Kernel มัก​คิด​ให้​ลงตัว​ได้​ยาก​ตั้งแต่​แรก​และ​ดูแล​ยาก เพราะ​ต้อง​อาศัย​การ​สื่อสาร​แบบ​เปิด​และ​การ​ตกลง​ร่วม​กัน​อย่าง​สม่ำเสมอ (ความ​ผูกพัน​แบบ Partnership อยู่​เบื้องหลัง) แต่​ก็​สำเร็จ​ได้​ถ้า​ทุก​ฝ่าย​เชื่อ​ว่าการ​มี kernel ร่วม​ดี​กว่า​การ​แยก​ทาง​เดิน (Separate WaysSeparate Waysประกาศ​ว่า Bounded Context นี้​ไม่​เชื่อม​ต่อ​กับ​อัน​อื่น​เลย เพื่อ​ให้​แต่ละ​ฝ่าย​หา​ทาง​แก้​แบบ​ง่าย ๆ เฉพาะ​ของ​ตน ใช้​เมื่อ​ค่า​ใช้​จ่าย​การ​เชื่อม​ต่อ​ไม่​คุ้มStrategic Design)

Customer-SupplierCustomer / Supplierความ​สัมพันธ์​ที่​ทีม​ต้นน้ำ (supplier) รับ​ความ​ต้องการ​ของ​ทีม​ปลาย​น้ำ (customer) เข้าไป​วางแผน​ด้วย มี​การ​เจรจา​และ​จัด​ลำดับ​งาน​ให้​ชัดเจนStrategic Design เป็น​รูปแบบ​ที่​พบ​บ่อย​และ​ดี​ต่อ​สุขภาพ​ที่สุด​อย่าง​หนึ่ง ทีม Supplier (ต้นน้ำ) เป็น​ฝ่าย​ผลิต ส่วน​ทีม Customer (ปลาย​น้ำ) เป็น​ฝ่าย​บริโภค จุด​เด่น​คือ Supplier ยอมรับฟัง​ความ​ต้องการ​ของ Customer แล้ว​นำ​เข้าไป​วางแผนการ​พัฒนา​ด้วย — มี​การ​เจรจา​และ​จัด​ลำดับ​งาน​ร่วม​กัน​อย่าง​ชัดเจน เพราะ Customer มี​น้ำหนัก​ใน​การ​ต่อรอง​พอสมควร นี่​คือ​ความ​สัมพันธ์​ที่​ลงตัว​เมื่อ​ทั้ง​สอง​ฝ่าย​เต็มใจ​ร่วมมือ

ConformistConformistทีม​ปลาย​น้ำ​ยอม “ตามใจ” model ของ​ทีม​ต้นน้ำ​ทั้งหมด​เพื่อ​ตัด​ความ​ซับซ้อน​ใน​การ​แปล​ภาษา ใช้​เมื่อ​ทีม​ต้นน้ำ​ไม่มี​แรง​จูงใจ​จะ​รองรับ​เราStrategic Design เกิด​เมื่อ​ทีม​ปลาย​น้ำ​พึ่งพา​ทีม​ต้นน้ำ แต่​ต้นน้ำ ไม่มี​แรง​จูงใจ จะ​รองรับ​ความ​ต้องการ​เฉพาะ​ของ​ปลาย​น้ำ​เลย ด้วย​เหตุผล​หลาย​อย่าง ทีม​ปลาย​น้ำ​อาจ​ประคอง​ความ​พยายาม​ใน​การ​แปล Ubiquitous LanguageUbiquitous Languageภาษา​กลาง​ที่​ทุก​คนใน​ทีม (ทั้ง​นัก​พัฒนา​และ​ผู้เชี่ยวชาญ​ธุรกิจ) ใช้​ร่วม​กัน อิง​กับ domain model โดยตรง คำ​เดียวกัน​ต้อง​หมาย​ถึง​สิ่ง​เดียวกัน และ​สะท้อน​ลง​ใน codeStrategic Design ของ model ต้นน้ำ​ให้​เข้า​กับ​ความ​ต้องการ​เฉพาะ​ของ​ตัวเอง​ไม่​ไหว ทีม​จึง​เลือก “ยอม​ตามใจ” — รับ​เอา Ubiquitous Language ของ​ต้นน้ำ​มา​ใช้​ทั้งดุ้น​ใน model ของ​ตัวเอง ทีม​มัก​กลาย​เป็น Conformist เมื่อ​ต้อง บูรณาการ​กับ model ขนาด​ใหญ่ ซับซ้อน และ​ตั้ง​มั่น​มา​นาน​แล้ว ตัวอย่าง​เช่น การ​ต้อง​ยอม​ตาม model ของ Amazon.com เมื่อ​เข้า​ร่วม​เป็น​หนึ่ง​ใน​ผู้​ขาย​แบบ affiliate ของ Amazon การ​ยอม​เป็น Conformist ตัด​ความ​ซับซ้อน​ใน​การ​แปล​ภาษา​ออก​ไป แต่​ก็​แลก​มา​ด้วย​การ​ที่ model ของ​เรา​ถูก​ผูก​ติด​กับ model ของ​คน​อื่น

Anticorruption LayerAnticorruption Layer (ACL)ชั้น​กั้น​ที่​ทีม​ปลาย​น้ำ​สร้าง​ขึ้น​เพื่อ​แปลง model ของ​ระบบ​อื่น​ให้​เป็น​ภาษา​ของ model ตัวเอง ป้องกัน​ไม่​ให้ model ภายนอก​มา “ปน​เปื้อน” model เราStrategic Design (ACL) คือ​ทาง​เลือก​ป้องกัน​ตัว​ที่​แข็งแรง​ที่สุด​ใน​กลุ่ม​ความ​สัมพันธ์​แบบ​บูรณาการ เป็น ชั้น​กั้น ที่​ทีม​ปลาย​น้ำ​สร้าง​ขึ้น​เพื่อ​แปลง model ของ​ระบบ​ต้นน้ำ​ให้​กลาย​เป็น​ภาษา​ของ model ตัวเอง บน​แผนภาพ​มัก​เขียน​กล่อง ACL คร่อม​ตรง​รอย​ต่อ​ฝั่ง​ปลาย​น้ำ ประโยชน์​คือ​ป้องกัน​ไม่​ให้​แนวคิด​และ​คำ​ศัพท์​จาก​ภายนอก​มา “ปน​เปื้อน” model ภายใน​ของ​เรา

ACL คือ​การ​ป้องกัน​ที่​คุ้ม​ค่าที่สุด​เมื่อ​ทำได้

เมื่อใด​ก็ตาม​ที่​ต้อง​เชื่อม​ต่อ​กับ​ระบบ​ที่​เรา​ควบคุม​ไม่​ได้ — โดย​เฉพาะ​ระบบ legacy ที่​ยุ่งเหยิง — ให้​สร้าง Anticorruption LayerAnticorruption Layer (ACL)ชั้น​กั้น​ที่​ทีม​ปลาย​น้ำ​สร้าง​ขึ้น​เพื่อ​แปลง model ของ​ระบบ​อื่น​ให้​เป็น​ภาษา​ของ model ตัวเอง ป้องกัน​ไม่​ให้ model ภายนอก​มา “ปน​เปื้อน” model เราStrategic Design กั้น​ไว้​เสมอ มัน​เป็น​วิธี​ที่​ปลอดภัย​ที่สุด​ใน​การบูรณาการ เพราะ​รักษา​ความ​สะอาด​ของ model ภายใน​เอา​ไว้​ได้ คำขวัญ​ของ​บท​นี้​คือ “อย่า​พูด​ภาษา​ของ​ระบบ​นั้น” ปล่อย​ให้ ACL เป็น​ล่าม​ให้

Open Host ServiceOpen Host Service (OHS)ทีม​ต้นน้ำ​เปิด​บริการ​เป็น​โพรโทคอล/API ที่​นิยาม​ไว้​ชัดเจน ให้​ทุก​คน​ที่​ต้อง​เชื่อม​ต่อ​มา​ใช้ได้ เหมาะ​กับ​ระบบ​ที่​มี​ผู้​ใช้​หลาย​ฝ่ายStrategic Design (OHS) คือ​มุมกลับ​ของ ACL — แทนที่​ปลาย​น้ำ​จะ​ป้องกัน​ตัว ฝ่าย ต้นน้ำ เป็น​ผู้​เปิด​บริการ​ของ​ตัวเอง​ออก​มา​เป็น​โพรโทคอล​หรือ API ที่​นิยาม​ไว้​อย่าง​ดี ให้​ทุก​ฝ่าย​ที่​ต้องการ​เชื่อม​ต่อ​เข้า​มา​ใช้ได้​ตาม​มาตรฐาน​เดียวกัน บน​แผนภาพ​มัก​เขียน​กล่อง OHS คร่อม​ตรง​รอย​ต่อ​ฝั่ง​ต้นน้ำ เหมาะ​กับ context ที่​ต้อง​ให้​บริการ​ผู้บริโภค​หลาย​ฝ่าย​พร้อม​กัน

Published LanguagePublished Languageภาษา​กลาง​ที่​จัด​ทำ​เป็น​เอกสาร​ชัดเจน ใช้​เป็น​สื่อ​แลกเปลี่ยน​ข้อมูล​ระหว่าง context เช่น มาตรฐาน​ข้อมูล​ของ​อุตสาหกรรม มัก​ใช้​คู่​กับ OHSStrategic Design คือ​ภาษา​กลาง​ที่ จัด​ทำ​เป็น​เอกสาร​ชัดเจน ใช้​เป็น​สื่อ​แลกเปลี่ยน​ข้อมูล​ข้าม context ที่​ผู้บริโภค​จำนวน​เท่าใด​ก็ตาม​นำ​ไป​ใช้​และ​แปล​ได้​ง่าย ผู้บริโภค​ที่​ทั้ง​อ่าน​และ​เขียน​สามารถ​แปล​เข้า/ออก​จาก​ภาษา​กลาง​นี้​ได้​อย่าง​มั่นใจ​ว่า​การบูรณาการ​ของ​ตน​ถูกต้อง Published Language แบบ​นี้​นิยาม​ได้​ด้วย XML Schema, JSON Schema หรือ wire format ที่​เหมาะสม​กว่า เช่น Protobuf หรือ Avro Published Language มัก​ไป​คู่​กับ Open Host ServiceOpen Host Service (OHS)ทีม​ต้นน้ำ​เปิด​บริการ​เป็น​โพรโทคอล/API ที่​นิยาม​ไว้​ชัดเจน ให้​ทุก​คน​ที่​ต้อง​เชื่อม​ต่อ​มา​ใช้ได้ เหมาะ​กับ​ระบบ​ที่​มี​ผู้​ใช้​หลาย​ฝ่ายStrategic Design เสมอ — OHS คือ “ช่อง​ทาง” ส่วน Published Language คือ “ภาษา” ที่​ไหล​ผ่าน​ช่อง​ทาง​นั้น บน​แผนภาพ​จึง​มัก​เขียน​คู่​กัน​เป็น OHS / PL

Separate WaysSeparate Waysประกาศ​ว่า Bounded Context นี้​ไม่​เชื่อม​ต่อ​กับ​อัน​อื่น​เลย เพื่อ​ให้​แต่ละ​ฝ่าย​หา​ทาง​แก้​แบบ​ง่าย ๆ เฉพาะ​ของ​ตน ใช้​เมื่อ​ค่า​ใช้​จ่าย​การ​เชื่อม​ต่อ​ไม่​คุ้มStrategic Design คือ​การ​ตัดสิน​ใจ​ว่า จะ​ไม่​เชื่อม​ต่อ​กัน​เลย เมื่อ​พิจารณา​แล้ว​พบ​ว่า​ค่า​ใช้​จ่าย​และ​ความ​ยุ่งยาก​ของ​การบูรณาการ​ไม่​คุ้ม​กับ​ประโยชน์​ที่​ได้ ทาง​ที่​ดี​กว่า​คือ​ปล่อย​ให้​แต่ละ Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design หา​ทาง​แก้​ปัญหา​แบบ​เฉพาะ​ของ​ตัวเอง​แบบ​ตรง​ไป​ตรง​มา ไป การ​ยอมรับ​ว่า “ไม่​ต้อง​ผูก​กัน” ก็​เป็นการ​ตัดสิน​ใจ​เชิงกลยุทธ์​ที่​ถูกต้อง​ได้

Big Ball of MudBig Ball of Mudระบบ​ที่​ปนเป​กัน​ยุ่งเหยิง​ไม่มี​ขอบเขต​ชัด ให้​ลาก​เส้น​ล้อม​ไว้​แล้ว​ยอมรับ​สภาพ ไม่​พยายาม​ทำ model ละเอียด​ใน​นั้น และ​ระวัง​ไม่​ให้​ลาม​ไป context อื่นStrategic Design คือ​ฝัน​ร้าย​ที่​พบ​ได้​ทั่วไป — ระบบ​ที่ model หลาย​อัน​ปนเป​กัน​ยุ่งเหยิง ไร้​ขอบเขต​ชัดเจน กฎ​และ​ข้อมูล​ที่​ควร​แยก​กัน​กลับ​พัน​กัน​ยุ่ง การ​แก้​ส่วน​หนึ่ง​ทำให้​อีก​หลาย​ส่วน​พัง คำ​แนะนำ​คือ​ให้ ลาก​เส้น​ล้อม​มัน​ไว้ แล้ว​ยอมรับ​สภาพ ไม่​พยายาม​สร้าง model ละเอียดอ่อน​ข้าง​ใน และ​ที่​สำคัญ​ที่สุด​คือ ระวัง​ไม่​ให้​ความ​เละเทะ​นั้น​ลาม​ออก​มา​ปน​เปื้อน context อื่น

เผื่อ​คำ​เตือน​ข้าง​ต้น​ยัง​ไม่​พอ นี่​คือ​สิ่ง​ที่​จะ​เกิด​ขึ้น​ต่อ​เนื่อง เมื่อ​คุณ​ต้อง​เป็น​เจ้าของ Big Ball of Mud:

  1. จำนวน AggregateAggregateกลุ่ม​ของ Entity และ Value Object ที่​ถูก​มอง​เป็น​หนึ่ง​หน่วย​เดียว​เพื่อ​รักษา​ความ​ถูกต้อง​ของ​ข้อมูล มี​ขอบเขต​ชัดเจน และ​เป็น​หน่วย​ของ transactionTactical Design ที่​เพิ่ม​ขึ้น​ต่อ​เนื่อง จะ ปน​เปื้อน​ข้าม​กัน (cross-contaminate) เพราะ​มี​การ​เชื่อม​โยง​และ​การ​พึ่งพา​กัน​อย่าง​ไม่​สมควร
  2. การ​ดูแล​ส่วน​หนึ่ง​ของ Big Ball of Mud ทำให้​เกิด​แรง​กระเพื่อม​ไป​ทั่ว​ทั้ง model นำ​ไป​สู่​ปัญหา​แบบ “ตี​ตัว​ตุ่น” (whack-a-mole) — กด​ตรง​นี้​โผล่​ตรง​นั้น
  3. มี​เพียง ความ​รู้​ใน​วง​แคบ (tribal knowledge) และ​การ​ลงแรง​แบบ​วีรบุรุษ — พูด​ทุก​ภาษา​พร้อม​กัน​ใน​คราว​เดียว — เท่านั้น​ที่​ยื้อ​ระบบ​ไว้​ไม่​ให้​ล่มสลาย​ทั้งหมด

ปัญหา​คือ​ทุก​วัน​นี้​โลก​ของ​ซอฟต์แวร์​เต็ม​ไป​ด้วย Big Ball of Mud อยู่​แล้ว และ​จำนวน​มี​แต่​จะ​เพิ่ม​ขึ้น​ทุก​เดือน ต่อ​ให้​เรา​หลีก​เลี่ยง​การ​สร้าง Big Ball of Mud ขึ้น​มา​เอง​ด้วย​เทคนิค DDD ได้​สำเร็จ ก็​ยัง​อาจ​ต้อง​เชื่อม​ต่อ​กับ​ของ​เก่า​ที่​มี​อยู่ ถ้า​จำเป็น​ต้อง​เชื่อม ให้​สร้าง Anticorruption LayerAnticorruption Layer (ACL)ชั้น​กั้น​ที่​ทีม​ปลาย​น้ำ​สร้าง​ขึ้น​เพื่อ​แปลง model ของ​ระบบ​อื่น​ให้​เป็น​ภาษา​ของ model ตัวเอง ป้องกัน​ไม่​ให้ model ภายนอก​มา “ปน​เปื้อน” model เราStrategic Design กั้น​ไว้​กับ​ระบบ legacy ทุก​ตัว เพื่อ​ปกป้อง model ของ​เรา​จาก​ความ​เละเทะ​ที่​จะ​ไหล​เข้า​มา ไม่​ว่า​จะ​ทำ​อะไร​ก็ตาม — อย่า​พูด​ภาษา​ของ​ระบบ​นั้น!

graph LR
  BBM["Big Ball of Mud<br/>(ระบบ legacy เละเทะ)"] -->|"U"| Edge["ACL"]
  Edge -->|"D"| Clean["model ของเรา<br/>(สะอาด)"]

interface แบบ​ไหน​ที่​จะ​ใช้​เชื่อม​ต่อ​กับ Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design หนึ่งๆ ขึ้น​อยู่​กับ​ว่า​ทีม​ที่​เป็น​เจ้าของ context นั้น​เปิด​อะไร​ให้​ใช้ อาจ​เป็น RPC ผ่าน SOAP, interface แบบ RESTful ที่​จัดการ​กับ resource หรือ​อาจ​เป็นการ​ส่ง​ข้อความ (messaging) ผ่าน​คิว​หรือ​แบบ Publish-Subscribe ใน​สถานการณ์​ที่​เลว​ร้าย​ที่สุด เรา​อาจ​ถูก​บังคับ​ให้​เชื่อม​ต่อ​ผ่าน​ฐาน​ข้อมูล​หรือ file ระบบ​โดยตรง — ซึ่ง​ควร​หลีก​เลี่ยง​ให้​ได้​มาก​ที่สุด โดย​เฉพาะ​การบูรณาการ​ผ่าน​ฐาน​ข้อมูล และ​ถ้า​จำเป็น​ต้องทำจริงๆ ก็​ต้อง​แยก model ที่​บริโภค​ข้อมูล​นั้น​ด้วย Anticorruption LayerAnticorruption Layer (ACL)ชั้น​กั้น​ที่​ทีม​ปลาย​น้ำ​สร้าง​ขึ้น​เพื่อ​แปลง model ของ​ระบบ​อื่น​ให้​เป็น​ภาษา​ของ model ตัวเอง ป้องกัน​ไม่​ให้ model ภายนอก​มา “ปน​เปื้อน” model เราStrategic Design เสมอ

ต่อ​ไป​จะ​ดู​สาม​วิธี​ที่​น่า​เชื่อถือ​กว่า เรียง​จาก “ทนทาน​น้อย​ที่สุด” ไป “ทนทาน​ที่สุด” คือ RPC, RESTful HTTP แล้ว​จึง​เป็น messaging

Remote Procedure Call (RPC) ทำงาน​ได้​หลาย​แบบ หนึ่ง​ใน​แบบ​ยอด​นิยม​คือ​ผ่าน SOAP (Simple Object Access Protocol) แนวคิด​เบื้องหลัง RPC with SOAP คือ​ทำให้การ​เรียก​ใช้​บริการ​จาก​อีก​ระบบ​หนึ่ง ดูเหมือน​การ​เรียก method ใน​เครื่อง​เรา (local call) ทั้ง​ที่​ความ​จริง request นั้น​ต้อง​เดิน​ทางข้าม​เครือข่าย ไป​ถึง​ระบบ​ปลายทาง ทำงาน​สำเร็จ แล้ว​ส่ง​ผลกลับ​มา​ทาง​เครือข่าย​อีก​ครั้ง

นี่​จึง​เปิด​ช่อง​ให้​เกิด ความ​ล้มเหลว​ของ​เครือข่าย​แบบ​สมบูรณ์ หรือ​อย่าง​น้อย​ก็​เกิด latency ที่​อาจ​คาด​ไม่​ถึง​ตอน​เริ่ม​ออกแบบ นอกจาก​นี้ RPC over SOAP ยัง ผูก​กัน​แน่น (strong coupling) ระหว่าง client Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design กับ context ที่​ให้​บริการ

graph LR
  Client["Client<br/>Bounded Context"] -->|"SOAP (XML)"| Service["Service<br/>Bounded Context"]
  WSDL["WSDL<br/>(บรรยายบริการ)"] -.->|describes| Service
  Registry[("Service Registry")] -.->|"finds service"| Client
  WSDL -.->|"points to description"| Registry

ปัญหา​หลัก​ของ RPC ไม่​ว่า​จะ​ใช้ SOAP หรือ​วิธี​อื่น​คือ​มัน ขาด​ความ​ทนทาน (lack robustness) ถ้า​เครือข่าย​มี​ปัญหา หรือ​ระบบ​ที่ host SOAP API มี​ปัญหา การ​เรียก​ที่​ดูเหมือน​ง่ายๆ นั้น​จะ​ล้มเหลว​ทั้งหมด เหลือ​ไว้​แต่​ผลลัพธ์​ที่​เป็น error อย่า​หลง​เชื่อ​ความ​ง่าย​ที่​เห็น​ภายนอก

แต่​เมื่อ RPC ทำงาน​ได้ — และ​ส่วน​ใหญ่​มัน​ก็​ทำงาน​ได้ — มัน​ก็​เป็น​วิธี​เชื่อม​ต่อ​ที่​มี​ประโยชน์​มาก ถ้า​เรา​มี​อิทธิพล​ต่อ​การ​ออกแบบ service context ก็​ควร​ผลักดัน​ให้​มัน​มี API ที่​ออกแบบ​ดี เป็น Open Host ServiceOpen Host Service (OHS)ทีม​ต้นน้ำ​เปิด​บริการ​เป็น​โพรโทคอล/API ที่​นิยาม​ไว้​ชัดเจน ให้​ทุก​คน​ที่​ต้อง​เชื่อม​ต่อ​มา​ใช้ได้ เหมาะ​กับ​ระบบ​ที่​มี​ผู้​ใช้​หลาย​ฝ่ายStrategic Design พร้อม Published LanguagePublished Languageภาษา​กลาง​ที่​จัด​ทำ​เป็น​เอกสาร​ชัดเจน ใช้​เป็น​สื่อ​แลกเปลี่ยน​ข้อมูล​ระหว่าง context เช่น มาตรฐาน​ข้อมูล​ของ​อุตสาหกรรม มัก​ใช้​คู่​กับ OHSStrategic Design และ​ไม่​ว่า​จะ​อย่างไร client context ของ​เรา​ก็​ควร​ออกแบบ​ให้​มี Anticorruption LayerAnticorruption Layer (ACL)ชั้น​กั้น​ที่​ทีม​ปลาย​น้ำ​สร้าง​ขึ้น​เพื่อ​แปลง model ของ​ระบบ​อื่น​ให้​เป็น​ภาษา​ของ model ตัวเอง ป้องกัน​ไม่​ให้ model ภายนอก​มา “ปน​เปื้อน” model เราStrategic Design กั้น​ไว้ เพื่อ​แยก model ของ​เรา​ออก​จาก​อิทธิพล​ภายนอก​ที่​ไม่​พึง​ประสงค์

graph LR
  Service["Service Bounded Context<br/>[OHS / PL]"] -->|"SOAP (XML) · U"| ACL["ACL"]
  ACL -->|"D"| Client["Client<br/>Bounded Context"]

การ​เชื่อม​ต่อ​ด้วย RESTful HTTP เน้น​ไป​ที่ resource ที่​แลกเปลี่ยน​กัน​ระหว่าง Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design รวม​ถึง4 operation หลัก: POST, GET, PUT และ DELETE หลาย​คน​พบ​ว่า REST ใช้งาน​ได้​ดี​เพราะ​ช่วย​ให้​นิยาม API สำหรับ​ระบบ​กระจาย (distributed computing) ได้​อย่าง​เหมาะเจาะ — และ​จะ​เถียง​ข้อ​นี้​ก็​ยาก เมื่อ​ดู​จาก​ความ​สำเร็จ​ของ​อินเทอร์เน็ต​และ​เว็บ

graph LR
  Service["Service Bounded Context<br/>[OHS / PL]"] -->|"RESTful HTTP · U"| ACL["ACL"]
  ACL -->|"D"| Client["Client<br/>Bounded Context"]

service Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design ที่​เปิด interface แบบ REST ควร​จัด​ให้​เป็น Open Host ServiceOpen Host Service (OHS)ทีม​ต้นน้ำ​เปิด​บริการ​เป็น​โพรโทคอล/API ที่​นิยาม​ไว้​ชัดเจน ให้​ทุก​คน​ที่​ต้อง​เชื่อม​ต่อ​มา​ใช้ได้ เหมาะ​กับ​ระบบ​ที่​มี​ผู้​ใช้​หลาย​ฝ่ายStrategic Design พร้อม Published LanguagePublished Languageภาษา​กลาง​ที่​จัด​ทำ​เป็น​เอกสาร​ชัดเจน ใช้​เป็น​สื่อ​แลกเปลี่ยน​ข้อมูล​ระหว่าง context เช่น มาตรฐาน​ข้อมูล​ของ​อุตสาหกรรม มัก​ใช้​คู่​กับ OHSStrategic Design — resource ทั้งหลาย​สมควร​ถูก​นิยาม​ให้​เป็น Published Language และ​เมื่อ​รวม​เข้า​กับ REST URI ของ​เรา มัน​จะ​กลาย​เป็น Open Host Service ที่​เป็น​ธรรมชาติ

RESTful HTTP มี​แนวโน้ม​จะ​ล้มเหลว​ด้วย​เหตุผล​เดียว​กับ RPC — เครือข่าย​ล่ม ผู้​ให้​บริการ​ล่ม หรือ latency ที่​คาด​ไม่​ถึง อย่างไร​ก็ตาม REST วาง​อยู่​บน​รากฐาน​ของ​อินเทอร์เน็ต และ​คง​ยาก​ที่​จะ​หา​ข้อ​ตำหนิ​ประวัติ​ของ​เว็บ​ใน​แง่​ความ​น่า​เชื่อถือ ความ​สามารถ​ใน​การ​ขยาย​ตัว และ​ความ​สำเร็จ​โดย​รวม

กับดัก​ที่​พบ​บ่อย​ใน REST: อย่า​ออกแบบ resource ให้​สะท้อน Aggregate ตรงๆ

ความ​ผิดพลาด​ที่​พบ​บ่อย​เมื่อ​ใช้ REST คือ​การ​ออกแบบ resource ให้​สะท้อน AggregateAggregateกลุ่ม​ของ Entity และ Value Object ที่​ถูก​มอง​เป็น​หนึ่ง​หน่วย​เดียว​เพื่อ​รักษา​ความ​ถูกต้อง​ของ​ข้อมูล มี​ขอบเขต​ชัดเจน และ​เป็น​หน่วย​ของ transactionTactical Design ใน model ของ​เรา​แบบตรงๆ การ​ทำ​เช่น​นี้​บังคับ​ให้ client ทุก​ตัว​ตก​อยู่​ใน​ความ​สัมพันธ์​แบบ ConformistConformistทีม​ปลาย​น้ำ​ยอม “ตามใจ” model ของ​ทีม​ต้นน้ำ​ทั้งหมด​เพื่อ​ตัด​ความ​ซับซ้อน​ใน​การ​แปล​ภาษา ใช้​เมื่อ​ทีม​ต้นน้ำ​ไม่มี​แรง​จูงใจ​จะ​รองรับ​เราStrategic Design — เพราะ​เมื่อ model เปลี่ยน​รูป resource ก็​เปลี่ยน​ตาม

ทาง​ที่​ถูก​คือ​ออกแบบ resource แบบ “สังเคราะห์” (synthetic) ตาม use case ที่​ขับ​เคลื่อน​โดย​ฝั่ง client — คือ resource ที่​ส่ง​ให้ client ต้อง​มี​รูปร่าง​และ​องค์​ประกอบ​ตรง​กับ สิ่ง​ที่ client ต้องการ ไม่ใช่​ตาม​หน้าตา​ของ model ภายใน บาง​ครั้ง model อาจ​บังเอิญ​หน้าตา​เหมือน​สิ่ง​ที่ client ต้องการ​พอดี แต่​ตัว​ขับ​เคลื่อน​การ​ออกแบบ resource ต้อง​เป็น “ความ​ต้องการ​ของ client” เสมอ ไม่ใช่​องค์​ประกอบ​ปัจจุบัน​ของ model

เมื่อ​ใช้ asynchronous messaging ใน​การ​เชื่อม​ต่อ เรา​ทำ​อะไร​ได้​มากมาย​เพียง​ให้ client Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design ไป subscribe กับ Domain EventDomain Eventสิ่ง​ที่ “เกิด​ขึ้น​แล้ว” ใน domain ซึ่ง​ส่วน​อื่น​สนใจ แทน​ด้วย​อ็อบเจ็กต์ที่​เปลี่ยนแปลง​ไม่​ได้ ตั้ง​ชื่อ​เป็น​อดีต เช่น CargoWasRouted ใช้​สื่อสาร​ข้าม AggregateTactical Design ที่ context ของ​เรา​เอง​หรือ context อื่น​เผยแพร่​ออก​มา การ​ใช้ messaging เป็น​หนึ่ง​ใน​รูปแบบ​การบูรณาการ​ที่ ทนทาน​ที่สุด เพราะ​ตัด​การ​ผูกพัน​ด้าน​เวลา (temporal coupling) ที่มา​กับ​การ​เรียก​แบบ blocking อย่าง RPC และ REST ออก​ไป ใน​เมื่อ​เรา​คาด​หวัง latency ใน​การ​แลกเปลี่ยน​ข้อความ​อยู่​แล้ว เรา​จึง​มัก​สร้าง​ระบบ​ที่​ทนทาน​กว่า เพราะ​ไม่​เคย​คาด​หวัง​ว่า​จะ​ได้​ผล​ทันที

graph LR
  subgraph Pub["Publishing Bounded Context"]
    PubAgg["Aggregate"] --> PubEvt["Domain Event"]
  end
  subgraph Sub["Subscribing Bounded Context"]
    SubAgg["Aggregate"]
  end
  PubEvt --> MM["Messaging Mechanism"]
  MM --> SubEvt["Domain Event"]
  SubEvt --> SubAgg

AggregateAggregateกลุ่ม​ของ Entity และ Value Object ที่​ถูก​มอง​เป็น​หนึ่ง​หน่วย​เดียว​เพื่อ​รักษา​ความ​ถูกต้อง​ของ​ข้อมูล มี​ขอบเขต​ชัดเจน และ​เป็น​หน่วย​ของ transactionTactical Design ตัว​หนึ่ง​ใน Bounded Context หนึ่ง​จะ​เผยแพร่ Domain EventDomain Eventสิ่ง​ที่ “เกิด​ขึ้น​แล้ว” ใน domain ซึ่ง​ส่วน​อื่น​สนใจ แทน​ด้วย​อ็อบเจ็กต์ที่​เปลี่ยนแปลง​ไม่​ได้ ตั้ง​ชื่อ​เป็น​อดีต เช่น CargoWasRouted ใช้​สื่อสาร​ข้าม AggregateTactical Design ออก​มา ซึ่ง​ฝ่าย​ใด​ก็ตาม​ที่​สนใจ​สามารถ​นำ​ไป​บริโภค​ได้ เมื่อ subscribing Bounded Context รับ event นั้น​มา ก็​จะ​ลงมือ​ทำ​อะไร​บาง​อย่าง​ตาม​ชนิด​และ​ค่า​ของ event — ปกติ​คือ​สร้าง Aggregate ใหม่ หรือ​แก้ไข Aggregate ที่​มี​อยู่​ใน context ที่​บริโภค​นั้น

ผู้บริโภค Domain Event เป็น Conformist หรือ​เปล่า?

อาจ​สงสัย​ว่า​ทำไม​การ​ให้ context อื่น​บริโภค Domain EventDomain Eventสิ่ง​ที่ “เกิด​ขึ้น​แล้ว” ใน domain ซึ่ง​ส่วน​อื่น​สนใจ แทน​ด้วย​อ็อบเจ็กต์ที่​เปลี่ยนแปลง​ไม่​ได้ ตั้ง​ชื่อ​เป็น​อดีต เช่น CargoWasRouted ใช้​สื่อสาร​ข้าม AggregateTactical Design จึง​ไม่​บังคับ​ให้​มัน​กลาย​เป็น ConformistConformistทีม​ปลาย​น้ำ​ยอม “ตามใจ” model ของ​ทีม​ต้นน้ำ​ทั้งหมด​เพื่อ​ตัด​ความ​ซับซ้อน​ใน​การ​แปล​ภาษา ใช้​เมื่อ​ทีม​ต้นน้ำ​ไม่มี​แรง​จูงใจ​จะ​รองรับ​เราStrategic Design คำ​ตอบ (ตาม​ที่ Vernon แนะนำ​ใน Implementing Domain-Driven Design บท​ที่ 13 “Integrating Bounded Contexts”) คือ ผู้บริโภค​ไม่​ควร​ใช้​ชนิด/class ของ event จาก​ผู้​เผยแพร่​โดยตรง แต่​ควร​พึ่งพา​เพียง schema ของ event เท่านั้น คือ Published LanguagePublished Languageภาษา​กลาง​ที่​จัด​ทำ​เป็น​เอกสาร​ชัดเจน ใช้​เป็น​สื่อ​แลกเปลี่ยน​ข้อมูล​ระหว่าง context เช่น มาตรฐาน​ข้อมูล​ของ​อุตสาหกรรม มัก​ใช้​คู่​กับ OHSStrategic Design หมายความ​ว่า ถ้า event เผยแพร่​ออก​มา​เป็น JSON (หรือ​รูปแบบ​ที่​ประหยัด​กว่า​นั้น) ผู้บริโภค​ควร parse มัน​เพื่อ​ดึง​ค่า​แอตทริบิวต์ออก​มา แทน​การ deserialize เป็น class ของ​ผู้​เผยแพร่

แน่นอน​ว่า​ทั้งหมด​นี้​ตั้ง​อยู่​บน​สมมติฐาน​ว่า subscribing context ได้​ประโยชน์​จาก​เหตุการณ์​ที่​เกิด​ขึ้น​เอง​ใน​ฝั่ง​ผู้​เผยแพร่​เสมอ แต่​บาง​ครั้ง client context ก็​จำเป็น​ต้อง ส่ง Command Message เชิง​รุก ไป​ยัง service context เพื่อ​บังคับ​ให้​เกิด​การกระทำ​บาง​อย่าง ใน​กรณี​เช่น​นี้ client context ก็​ยัง​จะ​ได้​รับ​ผลลัพธ์​กลับ​มา​ใน​รูป​ของ Domain Event ที่​ถูก​เผยแพร่​อยู่ดี

graph LR
  Cmd["Command"] --> SvcAgg
  subgraph Svc["Service Bounded Context"]
    SvcAgg["Aggregate"] --> SvcEvt["Domain Event"]
  end
  subgraph Cli["Client Bounded Context"]
    CliAgg["Aggregate"]
  end
  SvcEvt --> CliAgg
ทำ Asynchronous ด้วย REST ก็ได้

เรา​ทำ asynchronous messaging ผ่าน REST ได้​เช่น​กัน ด้วย​การ polling resource ที่​เติบโต​ขึ้น​ต่อ​เนื่อง ตาม​ลำดับ ใช้ background process ให้ client คอย poll resource ที่​เป็น Atom feed ของ​บริการ ซึ่ง​ให้​ชุด Domain EventDomain Eventสิ่ง​ที่ “เกิด​ขึ้น​แล้ว” ใน domain ซึ่ง​ส่วน​อื่น​สนใจ แทน​ด้วย​อ็อบเจ็กต์ที่​เปลี่ยนแปลง​ไม่​ได้ ตั้ง​ชื่อ​เป็น​อดีต เช่น CargoWasRouted ใช้​สื่อสาร​ข้าม AggregateTactical Design ที่​เพิ่ม​ขึ้น​ไม่​หยุด นี่​เป็น​วิธี​ที่​ปลอดภัย​ใน​การ​ดูแล operation แบบ asynchronous ระหว่าง​บริการ​กับ client พร้อม​ส่ง​มอบ event ล่าสุด​อย่าง​ต่อ​เนื่อง ถ้า​บริการ​ล่ม​ไป​ด้วย​เหตุ​ใด client ก็​แค่​ลอง retry ตาม​รอบ​ปกติ หรือ​ถอย​ห่าง​แล้ว​ค่อย​ลอง​ใหม่ จนกว่า feed จะ​กลับ​มา​ใช้ได้ Vernon อธิบาย​วิธี​นี้​อย่าง​ละเอียด​ใน Implementing Domain-Driven Design

เมื่อ client context (เรียก​ว่า C1) เชื่อม​ต่อ​กับ service context (S1) โดย​ปกติ C1 ไม่​ควร ส่ง request แบบ synchronous/blocking ไป​ยัง S1 ใน​ฐานะ​ผล​พวง​โดยตรง​ของ​การ​จัดการ request ที่​เข้า​มา​หา​มัน คือ ขณะ​ที่ client อีก​ตัว (C0) ส่ง blocking request มา​ที่ C1 อยู่ ก็​อย่า​ปล่อย​ให้ C1 ส่ง blocking request ต่อ​ไป​ยัง S1 — การ​ทำ​เช่น​นั้น​มี​โอกาส​สูง​มาก​ที่​จะ​เกิด “รถไฟ​ชน​กัน” (integration train wreck) ระหว่าง C0, C1 และ S1 หลีก​เลี่ยง​ได้​ด้วย​การ​ใช้ asynchronous messaging

ใน​ทุก​กรณี​ที่​ใช้ messaging คุณภาพ​ของ​ทางออก​โดย​รวม​จะ​ขึ้น​อยู่​กับ​คุณภาพ​ของ​กลไก messaging ที่​เลือก​อย่าง​มาก กลไก​นั้น​ควร​รองรับ At-Least-Once Delivery เพื่อ​รับประกัน​ว่า​ทุก​ข้อความ​จะ​ถูก​ส่ง​ถึง​ใน​ที่สุด ซึ่ง​ก็​แปล​ว่า subscribing Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design ต้อง​ถูก​สร้าง​ให้​เป็น Idempotent Receiver ด้วย

graph LR
  MM["Messaging Mechanism"] -->|"Message"| Recv["Receiver"]
  Recv -.->|"ACK"| MM

At-Least-Once Delivery คือ​รูปแบบ messaging ที่​กลไก​จะ ส่ง​ข้อความ​ซ้ำ​เป็น​ระยะ ใน​กรณี​ที่​ข้อความ​สูญหาย ผู้รับ​ตอบ​สนอง​ช้า​หรือ​ล่ม หรือ​ผู้รับ​ไม่​ส่ง acknowledge กลับ​มา ด้วย​การ​ออกแบบ​เช่น​นี้ จึง​เป็น​ไป​ได้ที่​ข้อความ​เดียวกัน​จะ​ถูก​ส่ง​ถึง​มากกว่า​หนึ่ง​ครั้ง แม้​ผู้​ส่ง​จะ​ส่ง​เพียง​ครั้ง​เดียว​ก็ตาม แต่​นั่น​ไม่ใช่​ปัญหา ถ้า​ผู้รับ​ถูก​ออกแบบ​มา​ให้​รับมือ​กับ​สถานการณ์​นี้​ได้

graph LR
  MM["Messaging Mechanism"] -->|"Open"| Recv["Receiver<br/>{ Status == Open }"]
  MM -->|"Open (ส่งซ้ำ)"| Recv

เมื่อใด​ที่​ข้อความ​อาจ​ถูก​ส่ง​มากกว่า​หนึ่ง​ครั้ง ผู้รับ​ต้อง​ถูก​ออกแบบ​ให้​จัดการ​ได้​อย่าง​ถูกต้อง Idempotent Receiver อธิบาย​ว่า​ผู้รับ request ทำ operation ใน​ลักษณะ​ที่ ให้​ผลลัพธ์​เหมือน​เดิม​เสมอ ไม่​ว่า​จะ​ทำซ้ำ​กี่​ครั้ง ดังนั้น​ถ้า​ข้อความ​เดียวกัน​มา​ถึง​หลาย​ครั้ง ผู้รับ​ก็​จัดการ​ได้​อย่าง​ปลอดภัย — อาจ​ใช้​การกรอง​ซ้ำ (de-duplication) แล้ว​เพิกเฉย​ข้อความ​ที่​ซ้ำ หรือ​ทำ operation ซ้ำ​โดย​ให้​ผลลัพธ์​ตรง​กับ​ครั้ง​ก่อน​เป๊ะ

เนื่องจาก​กลไก messaging นำ​การ​สื่อสาร​แบบ asynchronous Request-Response เข้า​มา​เสมอ การ​มี latency อยู่​บ้าง​จึง​เป็น​เรื่อง​ปกติ​และ​คาด​เดา​ได้ คำขอ​บริการ (เกือบ) ไม่​ควร block จนกว่า​บริการ​จะ​เสร็จ ดังนั้น​การ​ออกแบบ​โดย​คำนึง​ถึง messaging แปล​ว่า​เรา​วางแผน​เผื่อ latency ไว้​แต่​ต้น ซึ่ง​ทำให้​ทางออก​โดย​รวม​ทนทาน​ขึ้น​มาก​ตั้งแต่​แรก

🛒 เทียบ​กับ e-commerce

เมื่อ​ลูกค้า​กด​สั่ง​ซื้อ context “คำ​สั่ง​ซื้อ” เผยแพร่ Domain EventDomain Eventสิ่ง​ที่ “เกิด​ขึ้น​แล้ว” ใน domain ซึ่ง​ส่วน​อื่น​สนใจ แทน​ด้วย​อ็อบเจ็กต์ที่​เปลี่ยนแปลง​ไม่​ได้ ตั้ง​ชื่อ​เป็น​อดีต เช่น CargoWasRouted ใช้​สื่อสาร​ข้าม AggregateTactical Design ชื่อ OrderPlaced ออก​มา context “คลัง​สินค้า” และ “การ​จัด​ส่ง” ต่าง subscribe event นี้​เพื่อ​ทำงาน​ต่อ ถ้า​กลไก messaging ส่ง OrderPlaced ซ้ำ​เพราะ ACK หาย context “คลัง​สินค้า” ใน​ฐานะ Idempotent Receiver ต้อง​ไม่​ตัด​สต็อก​ซ้ำ — เช็ก​จาก order id ก่อน​ว่า​เคย​ประมวล​ผล​แล้ว​หรือ​ยัง


กลับ​มา​ที่​ตัวอย่าง​ระบบ​ประกันภัย​ที่​เคย​คุย​กัน​ใน​บท​ก่อน มี​คำถาม​ว่า “Policy ตัว​จริง” (policy of record) ควร​อยู่​ที่ context ไหน จำ​ได้​ว่า​มี​ชนิด Policy อยู่​ใน​สาม Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design ที่​ต่าง​กัน คือ Underwriting Context, Claims Context และ Inspections Context

graph TD
  UW["Underwriting Context<br/>(Policy)"]
  CL["Claims Context<br/>(Policy)"]
  IN["Inspections Context<br/>(Policy)"]

เป็น​ไป​ได้​ว่า “policy ตัว​จริง” ควร​อยู่​ที่​ฝ่าย Underwriting เพราะ​เป็น​จุด​ที่​มัน​ถือ​กำเนิด สมมติ​ว่า​ใช่ คำถาม​ต่อ​มา​คือ — แล้ว context อื่น​จะ​รู้​ถึง​การ​มี​อยู่​ของ​มัน​ได้​อย่างไร?

เมื่อ component ชนิด Policy ถูก​ออก​ใน Underwriting Context มัน​สามารถ​เผยแพร่ Domain EventDomain Eventสิ่ง​ที่ “เกิด​ขึ้น​แล้ว” ใน domain ซึ่ง​ส่วน​อื่น​สนใจ แทน​ด้วย​อ็อบเจ็กต์ที่​เปลี่ยนแปลง​ไม่​ได้ ตั้ง​ชื่อ​เป็น​อดีต เช่น CargoWasRouted ใช้​สื่อสาร​ข้าม AggregateTactical Design ชื่อ PolicyIssued ออก​มา ผ่าน​การ subscribe แบบ messaging context อื่น​ก็​จะ​ตอบ​สนอง​ต่อ event นั้น​ได้ ซึ่ง​อาจ​รวม​ถึง​การ​สร้าง component Policy ที่​สอดคล้อง​กัน​ขึ้น​ใน context ที่​บริโภค

graph TD
  UWPolicy["Policy"] --> Issued["PolicyIssued"]
  Issued -->|"causes creation"| CLPolicy["Policy<br/>(Claims Context)"]
  Issued -->|"causes creation"| INPolicy["Policy<br/>(Inspections Context)"]

PolicyIssued Domain Event จะ​บรรจุ identity ของ Policy ตัว​จริง ไว้ ซึ่ง​ใน​ที่​นี้​คือ policyId component ใด​ก็ตาม​ที่​ถูก​สร้าง​ขึ้น​ใน subscribing context จะ เก็บ identity นี้​ไว้ เพื่อ​ให้​สาวกลับ​ไป​ยัง Underwriting Context ต้นทาง​ได้ ใน​ตัวอย่าง​นี้ identity ถูก​บันทึก​ไว้​ใน​ชื่อ issuedPolicyId

graph LR
  subgraph SubC["Subscribing Context"]
    SubPolicy["Policy<br/>- issuedPolicyId"]
  end
  subgraph UWC["Underwriting Context"]
    UWPolicy["Policy<br/>- policyId"]
    IssuedData["Issued Policy Data"]
  end
  SubPolicy -->|"Query issuedPolicyId"| IssuedData

ถ้า​ต้องการ​ข้อมูล Policy มากกว่าที่ PolicyIssued Domain Event ให้​มา subscribing context สามารถ query กลับ ไป​ที่ Underwriting Context ได้​เสมอ โดย​ใช้ issuedPolicyId เป็น​กุญแจ​ใน​การ query

Enrichment vs Query-Back: เลือก​แบบ​ไหน​ดี?

บาง​ครั้ง​การ เติม​ข้อมูล​ให้​อ้วน (enrichment) — ใส่​ข้อมูล​ใน Domain EventDomain Eventสิ่ง​ที่ “เกิด​ขึ้น​แล้ว” ใน domain ซึ่ง​ส่วน​อื่น​สนใจ แทน​ด้วย​อ็อบเจ็กต์ที่​เปลี่ยนแปลง​ไม่​ได้ ตั้ง​ชื่อ​เป็น​อดีต เช่น CargoWasRouted ใช้​สื่อสาร​ข้าม AggregateTactical Design ให้​พอ​กับ​ความ​ต้องการ​ของ​ผู้บริโภค​ทุก​ฝ่าย — ก็​มี​ข้อดี เพราะ​ให้​ความ​เป็น​อิสระ (autonomy) แก่​ผู้บริโภค​มาก​ขึ้น ถ้า autonomy คือ​สิ่ง​ที่​ขับ​เคลื่อน​การ​ออกแบบ ให้​เลือก enrichment

แต่​อีก​ด้าน​หนึ่ง การ​คาด​เดา​ทุก​ชิ้น​ข้อมูล​ที่​ผู้บริโภค​ทุกราย​จะ​ต้องการ​นั้น​ยาก และ​อาจ enrich มาก​เกิน​ไป​จน​กลาย​เป็น​ปัญหา เช่น การ​ยัด​ข้อมูล​เยอะๆ ลง event อาจ​เป็นการ​ตัดสิน​ใจ​ที่​แย่​ด้าน​ความ​ปลอดภัย ถ้า​เป็น​เช่น​นั้น การ​ออกแบบ event แบบ บาง (thin) แล้ว​มี model สำหรับ query ที่​อุดม​และ​มี​ระบบ security ให้​ผู้บริโภค​ร้องขอ ก็​อาจ​เป็น​ทาง​เลือก​ที่​ต้องการ — และ​บาง​สถานการณ์​ก็​เรียกหา​ส่วนผสม​ที่​สมดุล​ของ​ทั้ง​สอง​แบบ

แล้ว​การ query กลับ​ไป​ยัง Underwriting Context จะ​ทำงาน​อย่างไร? เรา​ออกแบบ Open Host ServiceOpen Host Service (OHS)ทีม​ต้นน้ำ​เปิด​บริการ​เป็น​โพรโทคอล/API ที่​นิยาม​ไว้​ชัดเจน ให้​ทุก​คน​ที่​ต้อง​เชื่อม​ต่อ​มา​ใช้ได้ เหมาะ​กับ​ระบบ​ที่​มี​ผู้​ใช้​หลาย​ฝ่ายStrategic Design แบบ RESTful พร้อม Published LanguagePublished Languageภาษา​กลาง​ที่​จัด​ทำ​เป็น​เอกสาร​ชัดเจน ใช้​เป็น​สื่อ​แลกเปลี่ยน​ข้อมูล​ระหว่าง context เช่น มาตรฐาน​ข้อมูล​ของ​อุตสาหกรรม มัก​ใช้​คู่​กับ OHSStrategic Design บน Underwriting Context ได้ การ​ยิง HTTP GET ด้วย issuedPolicyId ก็​จะ​ดึง IssuedPolicyData กลับ​มา

graph LR
  subgraph UWC["Underwriting Context [OHS / PL]"]
    IssuedData["Issued Policy Data"]
  end
  Sub["Subscribing Context"]
  IssuedData -->|"GET /policies/{issuedPolicyId}"| Sub

ส่วน​รายละเอียด​ข้อมูล​ของ PolicyIssued Domain Event เอง — เช่น​ว่า​ควร​บรรจุ​อะไร​บ้าง — Vernon ยกยอด​ไป​อธิบาย​ใน​บท​ถัด​ไป​ที่​ว่าด้วย Tactical Design ด้วย Domain Events

กลับ​สู่​ตัวอย่าง Agile Project Management

อาจ​สงสัย​ว่า​เกิด​อะไร​ขึ้น​กับ​ตัวอย่าง Agile Project Management Context ที่​เคย​ใช้​ใน​บท​ก่อน การ​แวะ​ไป​ยัง domain ธุรกิจ​ประกันภัย​เป็นการ​เปิด​โอกาส​ให้​สำรวจ DDD ผ่าน​หลาย​ตัวอย่าง ซึ่ง​ช่วย​ให้​เข้าใจ DDD ได้​ดี​ขึ้น ไม่​ต้อง​ห่วง — บท​ถัด​ไป​จะ​กลับ​มา​ที่ Agile Project Management Context อีก​ครั้ง โดย​ตัวอย่าง​นั้น​มี Core DomainCore Domainsubdomain ที่​สำคัญ​และ​สร้าง​ความ​ได้​เปรียบ​ใน​การ​แข่งขัน​มาก​ที่สุด เป็น​เหตุผล​ที่​เรา​ต้อง​สร้าง​ซอฟต์แวร์​เอง​แทน​การ​ซื้อ ควร​ทุ่ม​ทีม​เก่ง​ที่สุด​ลง​ตรง​นี้Strategic Design เป็น Scrum (ประกอบ​ด้วย Product, BacklogItem, Release, Sprint, Task, Team ฯลฯ) เชื่อม​ต่อ​กับ context สนับสนุน​ต่างๆ ผ่าน Context Mapping


บท​นี้​พา​ทำความ​รู้จัก Context MappingContext Mappingกิจกรรมการ​ระบุ Bounded Context และ​นิยาม​ความ​สัมพันธ์​ระหว่าง​กัน (เช่น Partnership, Shared Kernel, ACL) เพื่อ​จัดการ​การ​เชื่อม​ต่อ​ระหว่าง​ทีม/ระบบStrategic Design อย่าง​รอบ​ด้าน เริ่ม​จาก ความ​สัมพันธ์​เก้า​แบบ บน Context MapContext Mapแผนภาพ​ที่​แสดง Bounded Context ทั้งหมด​และ​ความ​สัมพันธ์​ระหว่าง​กัน ช่วย​ให้​เห็น​ภาพ​รวม​และ​จุด​เชื่อม​ต่อ/แปล​ภาษา​ใน​ระบบ​ใหญ่Strategic DesignPartnershipPartnershipสอง​ทีม​ที่​หาก​ฝ่าย​ใด​ล้มเหลว​อีก​ฝ่าย​ก็​ส่ง​งาน​ไม่​ได้ จึง​ต้อง​วางแผน​และ​จัดการ​การ​เชื่อม​ต่อ​ร่วม​กัน feature ที่​พึ่งพา​กัน​ถูก​จัด​ให้​เสร็จ​ใน release เดียวกันStrategic Design ที่​ผูก​ชะตา​สอง​ทีม​เข้า​ด้วย​กัน, Shared KernelShared Kernelสอง​ทีม​ตกลง​แบ่ง model ส่วน​เล็ก ๆ มา​ใช้​ร่วม​กัน​โดย​มี​ขอบเขต​ชัดเจน ส่วน​นี้​ห้าม​แก้​โดย​ไม่​ปรึกษา​กัน เพราะ​กระทบ​ทั้ง​สอง​ฝ่ายStrategic Design ที่​แบ่ง model ส่วน​เล็ก​มา​ใช้​ร่วม, Customer-SupplierCustomer / Supplierความ​สัมพันธ์​ที่​ทีม​ต้นน้ำ (supplier) รับ​ความ​ต้องการ​ของ​ทีม​ปลาย​น้ำ (customer) เข้าไป​วางแผน​ด้วย มี​การ​เจรจา​และ​จัด​ลำดับ​งาน​ให้​ชัดเจนStrategic Design ที่​ต้นน้ำ​รับฟัง​ปลาย​น้ำ, ConformistConformistทีม​ปลาย​น้ำ​ยอม “ตามใจ” model ของ​ทีม​ต้นน้ำ​ทั้งหมด​เพื่อ​ตัด​ความ​ซับซ้อน​ใน​การ​แปล​ภาษา ใช้​เมื่อ​ทีม​ต้นน้ำ​ไม่มี​แรง​จูงใจ​จะ​รองรับ​เราStrategic Design ที่​ปลาย​น้ำ​ยอม​ตามใจ, Anticorruption LayerAnticorruption Layer (ACL)ชั้น​กั้น​ที่​ทีม​ปลาย​น้ำ​สร้าง​ขึ้น​เพื่อ​แปลง model ของ​ระบบ​อื่น​ให้​เป็น​ภาษา​ของ model ตัวเอง ป้องกัน​ไม่​ให้ model ภายนอก​มา “ปน​เปื้อน” model เราStrategic Design ที่​กั้น​แปล​ภาษา​เพื่อ​ปกป้อง model ของ​เรา, Open Host ServiceOpen Host Service (OHS)ทีม​ต้นน้ำ​เปิด​บริการ​เป็น​โพรโทคอล/API ที่​นิยาม​ไว้​ชัดเจน ให้​ทุก​คน​ที่​ต้อง​เชื่อม​ต่อ​มา​ใช้ได้ เหมาะ​กับ​ระบบ​ที่​มี​ผู้​ใช้​หลาย​ฝ่ายStrategic Design กับ Published LanguagePublished Languageภาษา​กลาง​ที่​จัด​ทำ​เป็น​เอกสาร​ชัดเจน ใช้​เป็น​สื่อ​แลกเปลี่ยน​ข้อมูล​ระหว่าง context เช่น มาตรฐาน​ข้อมูล​ของ​อุตสาหกรรม มัก​ใช้​คู่​กับ OHSStrategic Design ที่​ต้นน้ำ​เปิด​บริการ​ตาม​มาตรฐาน, Separate WaysSeparate Waysประกาศ​ว่า Bounded Context นี้​ไม่​เชื่อม​ต่อ​กับ​อัน​อื่น​เลย เพื่อ​ให้​แต่ละ​ฝ่าย​หา​ทาง​แก้​แบบ​ง่าย ๆ เฉพาะ​ของ​ตน ใช้​เมื่อ​ค่า​ใช้​จ่าย​การ​เชื่อม​ต่อ​ไม่​คุ้มStrategic Design ที่​เลือก​ไม่​เชื่อม​ต่อ​เลย และ Big Ball of MudBig Ball of Mudระบบ​ที่​ปนเป​กัน​ยุ่งเหยิง​ไม่มี​ขอบเขต​ชัด ให้​ลาก​เส้น​ล้อม​ไว้​แล้ว​ยอมรับ​สภาพ ไม่​พยายาม​ทำ model ละเอียด​ใน​นั้น และ​ระวัง​ไม่​ให้​ลาม​ไป context อื่นStrategic Design ที่​ต้อง​ล้อม​เส้น​และ​กัน​ไม่​ให้​ลาม

จาก​นั้น​ได้​เห็น​วิธี​นำ Context Mapping ไป​ใช้​จริง​ด้วย​กลไก​สาม​แบบ เรียง​จาก​ทนทาน​น้อย​ไป​มาก — RPC ผ่าน SOAP ที่​ดู​ง่าย​แต่​ผูก​กัน​แน่น​และ​ขาด​ความ​ทนทาน, RESTful HTTP ที่​เน้น resource และ4 operation หลัก (ระวัง​อย่า​ให้ resource สะท้อน AggregateAggregateกลุ่ม​ของ Entity และ Value Object ที่​ถูก​มอง​เป็น​หนึ่ง​หน่วย​เดียว​เพื่อ​รักษา​ความ​ถูกต้อง​ของ​ข้อมูล มี​ขอบเขต​ชัดเจน และ​เป็น​หน่วย​ของ transactionTactical Design ตรงๆ จน​กลาย​เป็น ConformistConformistทีม​ปลาย​น้ำ​ยอม “ตามใจ” model ของ​ทีม​ต้นน้ำ​ทั้งหมด​เพื่อ​ตัด​ความ​ซับซ้อน​ใน​การ​แปล​ภาษา ใช้​เมื่อ​ทีม​ต้นน้ำ​ไม่มี​แรง​จูงใจ​จะ​รองรับ​เราStrategic Design) และ Messaging ที่​ทนทาน​ที่สุด​เพราะ​ตัด temporal coupling ออก แต่​ต้อง​อาศัย At-Least-Once Delivery คู่​กับ Idempotent Receiver

สุดท้าย​ตัวอย่าง​ประกันภัย​ร้อย​ทุก​อย่าง​เข้า​ด้วย​กัน — Policy ตัว​จริง​อยู่​ที่ Underwriting Context เผยแพร่ Domain EventDomain Eventสิ่ง​ที่ “เกิด​ขึ้น​แล้ว” ใน domain ซึ่ง​ส่วน​อื่น​สนใจ แทน​ด้วย​อ็อบเจ็กต์ที่​เปลี่ยนแปลง​ไม่​ได้ ตั้ง​ชื่อ​เป็น​อดีต เช่น CargoWasRouted ใช้​สื่อสาร​ข้าม AggregateTactical Design PolicyIssued ให้ context อื่น​สร้าง Policy ของ​ตัวเอง​พร้อม​เก็บ issuedPolicyId ไว้​สาวกลับ และ​เมื่อ​ต้องการ​ข้อมูล​เพิ่ม​ก็ query กลับ​ผ่าน Open Host ServiceOpen Host Service (OHS)ทีม​ต้นน้ำ​เปิด​บริการ​เป็น​โพรโทคอล/API ที่​นิยาม​ไว้​ชัดเจน ให้​ทุก​คน​ที่​ต้อง​เชื่อม​ต่อ​มา​ใช้ได้ เหมาะ​กับ​ระบบ​ที่​มี​ผู้​ใช้​หลาย​ฝ่ายStrategic Design แบบ REST นี่​คือ​รากฐาน​ที่​จะ​ต่อยอด​ประสบการณ์ Context Mapping ของ​เรา​ต่อ​ไป

สรุป​ประเด็น​สำคัญ
  • Context MapContext Mapแผนภาพ​ที่​แสดง Bounded Context ทั้งหมด​และ​ความ​สัมพันธ์​ระหว่าง​กัน ช่วย​ให้​เห็น​ภาพ​รวม​และ​จุด​เชื่อม​ต่อ/แปล​ภาษา​ใน​ระบบ​ใหญ่Strategic Design นิยาม​ทั้ง ความ​สัมพันธ์​ระหว่าง​ทีม และ กลไก​ทาง​เทคนิค ระหว่าง​สอง Bounded ContextBounded Contextขอบเขต​ที่​ชัดเจน (มัก​เป็น​ระบบ​ย่อย​หรือ​ทีม​หนึ่ง) ที่ model หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ภายใน​ขอบเขต​นี้ Ubiquitous Language สอดคล้อง​กัน 100%Strategic Design ที่​เชื่อม​กัน — ทิศ​ของ​อิทธิพล​คือ ต้นน้ำ (U) → ปลาย​น้ำ (D)Upstream / Downstreamความ​สัมพันธ์​ที่​การกระทำ​ของ​ฝ่าย​ต้นน้ำ (upstream) ส่ง​ผล​ต่อ​ฝ่าย​ปลาย​น้ำ (downstream) แต่​ไม่ใช่​ใน​ทาง​กลับ​กัน — ทิศ​ของ​อิทธิพลStrategic Design
  • ความ​สัมพันธ์​เก้า​แบบ: PartnershipPartnershipสอง​ทีม​ที่​หาก​ฝ่าย​ใด​ล้มเหลว​อีก​ฝ่าย​ก็​ส่ง​งาน​ไม่​ได้ จึง​ต้อง​วางแผน​และ​จัดการ​การ​เชื่อม​ต่อ​ร่วม​กัน feature ที่​พึ่งพา​กัน​ถูก​จัด​ให้​เสร็จ​ใน release เดียวกันStrategic Design, Shared KernelShared Kernelสอง​ทีม​ตกลง​แบ่ง model ส่วน​เล็ก ๆ มา​ใช้​ร่วม​กัน​โดย​มี​ขอบเขต​ชัดเจน ส่วน​นี้​ห้าม​แก้​โดย​ไม่​ปรึกษา​กัน เพราะ​กระทบ​ทั้ง​สอง​ฝ่ายStrategic Design, Customer-SupplierCustomer / Supplierความ​สัมพันธ์​ที่​ทีม​ต้นน้ำ (supplier) รับ​ความ​ต้องการ​ของ​ทีม​ปลาย​น้ำ (customer) เข้าไป​วางแผน​ด้วย มี​การ​เจรจา​และ​จัด​ลำดับ​งาน​ให้​ชัดเจนStrategic Design, ConformistConformistทีม​ปลาย​น้ำ​ยอม “ตามใจ” model ของ​ทีม​ต้นน้ำ​ทั้งหมด​เพื่อ​ตัด​ความ​ซับซ้อน​ใน​การ​แปล​ภาษา ใช้​เมื่อ​ทีม​ต้นน้ำ​ไม่มี​แรง​จูงใจ​จะ​รองรับ​เราStrategic Design, Anticorruption LayerAnticorruption Layer (ACL)ชั้น​กั้น​ที่​ทีม​ปลาย​น้ำ​สร้าง​ขึ้น​เพื่อ​แปลง model ของ​ระบบ​อื่น​ให้​เป็น​ภาษา​ของ model ตัวเอง ป้องกัน​ไม่​ให้ model ภายนอก​มา “ปน​เปื้อน” model เราStrategic Design, Open Host ServiceOpen Host Service (OHS)ทีม​ต้นน้ำ​เปิด​บริการ​เป็น​โพรโทคอล/API ที่​นิยาม​ไว้​ชัดเจน ให้​ทุก​คน​ที่​ต้อง​เชื่อม​ต่อ​มา​ใช้ได้ เหมาะ​กับ​ระบบ​ที่​มี​ผู้​ใช้​หลาย​ฝ่ายStrategic Design, Published LanguagePublished Languageภาษา​กลาง​ที่​จัด​ทำ​เป็น​เอกสาร​ชัดเจน ใช้​เป็น​สื่อ​แลกเปลี่ยน​ข้อมูล​ระหว่าง context เช่น มาตรฐาน​ข้อมูล​ของ​อุตสาหกรรม มัก​ใช้​คู่​กับ OHSStrategic Design, Separate WaysSeparate Waysประกาศ​ว่า Bounded Context นี้​ไม่​เชื่อม​ต่อ​กับ​อัน​อื่น​เลย เพื่อ​ให้​แต่ละ​ฝ่าย​หา​ทาง​แก้​แบบ​ง่าย ๆ เฉพาะ​ของ​ตน ใช้​เมื่อ​ค่า​ใช้​จ่าย​การ​เชื่อม​ต่อ​ไม่​คุ้มStrategic Design, Big Ball of MudBig Ball of Mudระบบ​ที่​ปนเป​กัน​ยุ่งเหยิง​ไม่มี​ขอบเขต​ชัด ให้​ลาก​เส้น​ล้อม​ไว้​แล้ว​ยอมรับ​สภาพ ไม่​พยายาม​ทำ model ละเอียด​ใน​นั้น และ​ระวัง​ไม่​ให้​ลาม​ไป context อื่นStrategic Design
  • เจอ legacy เละเทะ​ให้​กั้น​ด้วย Anticorruption LayerAnticorruption Layer (ACL)ชั้น​กั้น​ที่​ทีม​ปลาย​น้ำ​สร้าง​ขึ้น​เพื่อ​แปลง model ของ​ระบบ​อื่น​ให้​เป็น​ภาษา​ของ model ตัวเอง ป้องกัน​ไม่​ให้ model ภายนอก​มา “ปน​เปื้อน” model เราStrategic Design เสมอ — “อย่า​พูด​ภาษา​ของ​ระบบ​นั้น”
  • กลไก​เชื่อม​ต่อ​เรียง​จาก​ทนทาน​น้อย​ไป​มาก: RPC/SOAP → RESTful HTTP → Messaging โดย messaging ที่​ดี​ต้อง​มี At-Least-Once Delivery + Idempotent Receiver
  • บูรณาการ​ที่​ปลอดภัย​ที่สุด​คือ​ให้ context หนึ่ง​เผยแพร่ Domain EventDomain Eventสิ่ง​ที่ “เกิด​ขึ้น​แล้ว” ใน domain ซึ่ง​ส่วน​อื่น​สนใจ แทน​ด้วย​อ็อบเจ็กต์ที่​เปลี่ยนแปลง​ไม่​ได้ ตั้ง​ชื่อ​เป็น​อดีต เช่น CargoWasRouted ใช้​สื่อสาร​ข้าม AggregateTactical Design แล้ว​อีก context ไป subscribe โดย​พึ่งพา​แค่ schema (Published LanguagePublished Languageภาษา​กลาง​ที่​จัด​ทำ​เป็น​เอกสาร​ชัดเจน ใช้​เป็น​สื่อ​แลกเปลี่ยน​ข้อมูล​ระหว่าง context เช่น มาตรฐาน​ข้อมูล​ของ​อุตสาหกรรม มัก​ใช้​คู่​กับ OHSStrategic Design) ไม่ใช่ class ของ​ผู้​เผยแพร่

🔗 อ้างอิง​เพิ่มเติม​ใน DevIQ

เจาะ​ลึก​แนวคิด​ใน​บท​นี้​ต่อ​ได้ที่​คลัง​อ้างอิง DevIQ:

  • Context Mapping — ภาพ​รวม​ของ​แนวคิด Context Map และ​ความ​สัมพันธ์​ทั้ง​เก้า​แบบ​ที่​บท​นี้​อธิบาย​โดย​ละเอียด
  • Anti-Corruption Layer — สรุป​แก่น​ของ ACL ที่​บท​นี้​ยก​เป็น “ทาง​เลือก​ป้องกัน​ตัว​ที่​แข็งแรง​ที่สุด” เมื่อ​ต้อง​เชื่อม​กับ​ระบบ​ภายนอก​หรือ legacy
  • Shared Kernel — ขยาย​ความ​รูปแบบ Shared Kernel หนึ่ง​ใน​เก้า​ความ​สัมพันธ์​บน Context Map ที่​ต้อง​อาศัย​ความ​ตกลง​ร่วม​กัน​อย่าง​ใกล้​ชิด
  • Domain Events — กลไก​เบื้องหลัง​การ​เชื่อม​ต่อ​แบบ Messaging ที่​บท​นี้​ใช้​ใน​ตัวอย่าง PolicyIssued

เช็กความเข้าใจ — บทที่ 4

ข้อ 1 / 4

Anticorruption Layer (ACL) ทำหน้าที่อะไร และฝ่ายใดเป็นผู้สร้าง?