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

Strategic Design

มอง domain จาก​มุม​สูง วาง​รากฐาน​ของ model ก่อน​ลง​รายละเอียด

การ​จำลอง domain ใน DDD มี 2 เฟส​ที่​เสริม​กัน — strategic design และ tactical design Strategic Design คือ​ชุด​กิจกรรม​และ pattern ระดับ​สูง​ที่​ตอบ​คำถาม “ระบบ​นี้​ประกอบ​ด้วย​ส่วน​อะไร​บ้าง แต่ละ​ส่วน​สำคัญ​แค่​ไหน และ​ส่วน​เหล่า​นั้น​สื่อสาร​กัน​อย่างไร” — ก่อน​ที่​จะ​ลง​ไป​ออกแบบ entity, value object หรือ aggregate ภายใน​แต่ละ​ส่วน

Eric Evans อธิบาย​ไว้​ใน “Domain-Driven Design” (2003) ว่า​ระบบ​ขนาด​ใหญ่​ไม่มี​ทาง​มี model เดียว​ที่ “รวม​เป็น​หนึ่ง” ครอบคลุม​ทั้ง​องค์กร​ได้​อย่าง​คุ้ม​ค่า เพราะ​แต่ละ​ส่วน​ของ​ธุรกิจ​มัก​ใช้​คำ​ศัพท์​เดียวกัน​ใน​ความหมาย​ต่าง​กัน (polyseme) Strategic Design จึง​เป็น​เครื่องมือ​จัดการ​ความ​ซับซ้อน​ระดับ​องค์กร โดย​ทำ​สาม​เรื่อง​หลัก​ไป​พร้อม​กัน

  1. แบ่ง domain ใหญ่ (domain) เป็น subdomain และ​จัด​ลำดับ​ความ​สำคัญ​ว่า​ส่วน​ไหน​คือ core, supporting หรือ generic
  2. นิยาม bounded context — ขอบเขต​ของ​แต่ละ model ย่อย​และ ubiquitous language ของ​มัน
  3. วาง Context Mapping — อธิบาย​ความ​สัมพันธ์​และ​การ​สื่อสาร​ระหว่าง bounded context เหล่า​นั้น

เทคนิค​ที่​ใช้​ค้นหา​ข้อมูล​ใน​เฟส​นี้​ได้แก่ Domain Storytelling และ EventStorming ซึ่ง​รวบรวม​รายละเอียด​จาก domain เอ็กซ์​เพิร์ต​และ​ทีม​งาน เพื่อ​สร้าง model domain เชิง​แนวคิด (conceptual domain model) ก่อน​ที่​จะ​ตัดสิน​ใจ​เรื่อง code หรือ​เทคโนโลยี​ใด ๆ

Strategic Design ตอบ​โจทย์​ที่ tactical design เพียง​อย่าง​เดียว​แก้​ไม่​ได้ นั่น​คือ​ปัญหา​ระดับ “องค์กร​และ​ทีม” ไม่ใช่​ระดับ “class และอ็อบเจ็กต์”

  • จัด​ลำดับ​ความ​สำคัญ​ของ​การ​ลงทุน — ผ่าน​การ​จำแนก subdomain เป็น core (สร้าง​ความ​ได้​เปรียบ​ทางการ​แข่งขัน ควร​ลงแรง​ออกแบบ​มาก​ที่สุด), supporting (จำเป็นต่อ​ธุรกิจ​แต่​ไม่ใช่​จุด​ต่าง​ที่​ทำให้​ชนะ​คู่แข่ง) และ generic (ปัญหา​ที่​อุตสาหกรรม​แก้​ไว้​แล้ว ควร​ซื้อ​หรือ​ใช้​ของ​สำเร็จรูป​แทน​สร้าง​เอง)
  • ป้องกัน model ปน​เปื้อน​กัน — bounded context แต่ละ​อัน​มี ubiquitous language ของ​ตัวเอง คำ​ว่า “Customer” ใน Billing context ไม่​จำเป็น​ต้อง​มี field เหมือน​กับ “Customer” ใน Support context การ​พยายาม​ยัด​ทุก​ความหมาย​ไว้​ใน class เดียว​มัก​จบ​ด้วย Blob ที่​ทุก​ทีม​กลัว​จะ​แก้
  • กำหนด​หน่วย​ของ​สถาปัตยกรรม​และ​ทีม — Context Mapping ระบุ pattern ความ​สัมพันธ์​ระหว่าง​บริบท เช่น Shared Kernel, Customer-Supplier, Conformist, Open Host Service และ Anti-Corruption Layer ซึ่ง​สอดคล้อง​กับ Conway’s Law — โครงสร้าง​การ​สื่อสาร​ของ​ทีม​มัก​สะท้อน​อยู่​ใน​โครงสร้าง​ของ​ระบบ
  • เป็น​จุด​เริ่มต้น​ของ​การ​ตัด​ขอบเขต microservice — แนวทาง​ของ Microsoft Azure Architecture Center แนะนำ​ว่า microservice หนึ่ง​ไม่​ควร​ครอบคลุม​มากกว่า1 bounded context และ​ไม่​ควร​เล็ก​กว่า1 aggregate strategic design (วิเคราะห์ domain → นิยาม bounded context) จึง​มา​ก่อน​ขั้นตอน tactical design (นิยาม entity/aggregate/service) เสมอ​ใน​กระบวนการ​นี้

ข้อ​ควร​ระวัง Strategic Design ไม่ใช่ Big Design Up Front (BDUF) มัน​เป็น​กิจกรรม​ที่​ทำซ้ำ​ได้ (iterative) — เมื่อ​ทีม​เข้าใจ domain มาก​ขึ้น ขอบเขต bounded context ก็​ปรับ​เปลี่ยน​ได้ Vaughn Vernon ย้ำ​ใน “Implementing Domain-Driven Design” (2013) ว่า strategic patterns (bounded context, context map, ubiquitous language) ควร​มา​ก่อน tactical patterns เสมอ เพราะ​ถ้า​ขอบเขต​ผิด​ตั้งแต่​แรก การ​ออกแบบ entity ที่​ดี​เพียง​ใด​ก็​แก้​ปัญหา​พื้นฐาน​ไม่​ได้

สมมติ​บริษัท Fabrikam เปิด​บริการ​ส่ง​ของ​ด้วย​โด​รน (ตัวอย่าง​จาก Microsoft Azure Architecture Center) ทีม​เริ่ม​จาก strategic design ก่อน​เขียน code สัก​บรรทัด

  1. วิเคราะห์ domain — ระดม​ความเห็น​กับ domain เอ็กซ์​เพิร์ต​จน​ได้​แผนภาพ​ความ​สัมพันธ์​ของ function ทาง​ธุรกิจ: Shipping, Drone Management, ETA Analysis, Accounts, Invoicing, Call Center ฯลฯ
  2. จำแนก subdomain — Shipping และ Drone Management เป็น core subdomain (ต้อง​ลงทุน​ออกแบบ​ละเอียด​ที่สุด​เพราะ​คือ​จุด​ต่าง​ของ​ธุรกิจ) Invoicing เป็น supporting subdomain ส่วน User Accounts และ Call Center เป็น generic subdomain ที่​ใช้​ระบบ​สำเร็จรูป​ได้​เลย
  3. นิยาม bounded context — แบ่ง​เป็น Shipping context, Drone Management context, Accounts context โดย​แต่ละ​อัน​มี model Drone ของ​ตัวเอง​ที่​ไม่​จำเป็น​ต้อง​เหมือน​กัน (ระบบ​ซ่อม​บำรุง​สนใจ mileage/maintenance history ส่วน​ระบบ​จัด​ส่ง​สนใจ​แค่ availability/ETA)
  4. วาง context map — ระบุ​ว่า Shipping พึ่งพา Accounts แบบ Customer-Supplier และ​พึ่งพา Drone Management ผ่าน Open Host Service

code C# ด้าน​ล่าง​แสดง​ผลลัพธ์​ของ​การ​ตัดสิน​ใจ​เชิง strategic: แต่ละ bounded context มี​นิยาม Drone เป็น​ของ​ตัวเอง และ​มี translator ทำ​หน้าที่​แปลง​ข้อมูล​ข้าม​ขอบเขต — นี่​คือ​สิ่ง​ที่ tactical design จะ “ลง​รายละเอียด” ต่อ หลัง​จาก strategic design “ตัด​ขอบเขต” ไว้​แล้ว

// ===== Shipping bounded context =====
// สนใจแค่ว่าโดรนว่างหรือไม่ และ ETA เท่านั้น
namespace Shipping.Domain
{
public sealed class AvailableDrone
{
public Guid DroneId { get; }
public bool IsAvailable { get; }
public TimeSpan EstimatedTimeToPickup { get; }
public AvailableDrone(Guid droneId, bool isAvailable, TimeSpan estimatedTimeToPickup)
{
DroneId = droneId;
IsAvailable = isAvailable;
EstimatedTimeToPickup = estimatedTimeToPickup;
}
}
}
// ===== Drone Management bounded context =====
// สนใจรายละเอียดเชิงกายภาพและประวัติซ่อมบำรุงของโดรน
namespace DroneManagement.Domain
{
public class Drone
{
public Guid DroneId { get; }
public string ModelNumber { get; }
public double MileageKm { get; private set; }
public DateTime LastMaintenanceDate { get; private set; }
public bool IsGrounded { get; private set; }
public Drone(Guid droneId, string modelNumber)
{
DroneId = droneId;
ModelNumber = modelNumber;
}
public void ScheduleMaintenance()
{
// logic เชิง domain ที่ Shipping context ไม่จำเป็นต้องรู้เลย
IsGrounded = true;
LastMaintenanceDate = DateTime.UtcNow;
}
}
// translator ที่ open host service ของ Drone Management เปิดให้ context อื่นเรียกใช้
public static class DroneToAvailabilityTranslator
{
public static Shipping.Domain.AvailableDrone ToAvailableDrone(Drone drone, TimeSpan eta)
{
// จงใจคัดลอกเฉพาะข้อมูลที่ Shipping context ต้องใช้ ไม่ยัด field ภายในเข้าไป
return new Shipping.Domain.AvailableDrone(
drone.DroneId,
isAvailable: !drone.IsGrounded,
estimatedTimeToPickup: eta);
}
}
}

แผนภาพ​ต่อ​ไป​นี้​แสดง​ลำดับ​กิจกรรม​ของ strategic design ตั้งแต่​วิเคราะห์ domain จนถึง​ส่ง​ต่อ​ให้ tactical design

graph LR
    Domain[Domain analysis]
    Subdomain[Subdomain classification]
    BC[Bounded context definition]
    CtxMap[Context mapping]
    UL[Ubiquitous language per context]
    Tactical[Tactical design]

    Domain --> Subdomain
    Subdomain --> BC
    BC --> UL
    BC --> CtxMap
    CtxMap --> Tactical
    UL --> Tactical
  • Tactical Design — Strategic Design ตอบ​คำถาม “ขอบเขต​อยู่​ตรง​ไหน” ส่วน Tactical Design ตอบ​คำถาม “ภายใน​ขอบเขต​นั้น​ออกแบบ code อย่างไร” ทั้ง​สอง​เฟส​ทำงาน​คู่​กัน​เสมอ — strategic มา​ก่อน​เพื่อ​กำหนด​กรอบ แล้ว tactical จึง​ลง​รายละเอียด
  • Domain และ Subdomaindomain คือ​พื้นที่​ปัญหา​ทั้งหมด ซึ่ง strategic design วิเคราะห์​แตก​เป็น subdomain (core/supporting/generic) เพื่อ​จัด​ลำดับ​ความ​สำคัญ​ของ​การ​ลงทุน
  • Bounded Context และ Ubiquitous Language — ผลลัพธ์​หลัก​ของ strategic design คือ​การ​นิยาม bounded context แต่ละ​อัน​พร้อม ubiquitous language ของ​ตัวเอง
  • Context Mapping — เมื่อ​มี​หลาย bounded context strategic design ใช้ Context Mapping อธิบาย​ความ​สัมพันธ์​ระหว่าง​กัน เช่น Customer-Supplier, Shared Kernel, Anti-Corruption Layer
  • EventStorming และ Domain Storytelling — เทคนิค​เชิง​ปฏิบัติ​ที่​ใช้​ใน​เฟส strategic design เพื่อ​ดึง​ความ​รู้ domain จาก​ผู้เชี่ยวชาญ ได้แก่ EventStorming และ Domain Storytelling
  • Conway’s Law — ขอบเขต​ที่ strategic design กำหนด​ไว้​มัก​สอดคล้อง​กับ​โครงสร้าง​ทีม​ตาม Conway’s Law และ​มัก​ใช้​เป็น​เกณฑ์​ตัด​ขอบเขต microservice ใน​สถาปัตยกรรม​สมัย​ใหม่