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

จาก Event Storming สู่ Bounded Context และ code

ห้า​บท​ที่​ผ่าน​มา​เรา​แปะ Post-it สี​ส้ม​เต็ม​ผนัง เรียง Domain EventDomain Eventสิ่ง​ที่ “เกิด​ขึ้น​แล้ว” และ​มี​ความหมาย​ต่อ​ผู้เชี่ยวชาญ​ธุรกิจ เขียน​เป็นกริยา​อดีต (เช่น OrderPlaced, PaymentCaptured) แทน​ด้วย Post-it สี​ส้ม เป็น​หน่วย​ตั้งต้น​ของ EventStorming ทุก​แบบTactical Design เป็น​ไทม์​ไลน์ เติม CommandCommandความ​ตั้งใจ​หรือ​การ​ตัดสิน​ใจ​ที่ “สั่ง​ให้​เกิด” domain event เขียน​เป็นกริยา​คำ​สั่ง (เช่น PlaceOrder) แทน​ด้วย Post-it สี​ฟ้า มัก​มี actor เป็น​ผู้​ลงมือTactical Design กับ ActorActorคน/บทบาท​ที่​ออก command (เช่น ลูกค้า ร้าน​อาหาร ไร​เด​อร์) แทน​ด้วย Post-it สี​เหลือง​แผ่น​เล็ก​แปะ​มุม​ของ command ช่วย​ตอบ​ว่า “ใคร​เป็น​คน​ทำ”Process เข้า​มา หา AggregateAggregateกลุ่ม​ของ object ที่​ถือ​กฎ​ความ​สอดคล้อง (invariant) ไว้​ด้วย​กัน ใน EventStorming คือ “สิ่ง​ที่​รับ command แล้ว​ปล่อย event ออก​มา” (เช่น Order, Payment) แทน​ด้วย Post-it สี​เหลือง​แผ่น​ใหญ่Tactical Design ที่​รับคำ​สั่ง​แล้ว​ปล่อย event ลาก PolicyPolicyกฎ​เชิง​ปฏิกิริยา​แบบ “whenever X → do Y” ที่​คอย​ฟัง event หนึ่ง​แล้ว​สั่ง command ต่อ​โดย​อัตโนมัติ (เช่น เมื่อ PaymentCaptured → แจ้ง​ร้าน​ให้​กด​รับ) แทน​ด้วย Post-it สี​ม่วง​อ่อนProcess เชื่อม event กับ command ถัด​ไป และ​แปะ HotspotHotspotจุด​ที่​เป็น​ปัญหา ข้อ​ขัดแย้ง หรือ​คำถาม​ที่​ยัง​ตอบ​ไม่​ได้​ระหว่าง​เวิร์กช็อป แทน​ด้วย Post-it สี​แดง​วาง​เฉียง ๆ เก็บ​ไว้​กลับ​มา​คุย​ทีหลัง​โดย​ไม่​ขัดจังหวะ​การ​ไหล​ของ​กลุ่มProcess สี​แดง​ตรง​จุด​ที่​ยัง​ตอบ​ไม่​ได้ ตอน​นี้​ผนัง​เต็ม​ไป​ด้วย​ความ​รู้​ที่​เมื่อ​ก่อน​กระจาย​อยู่​ใน​หัว​คน​หลาย​ฝ่าย คำถาม​ของ​บท​สุดท้าย​คือ แล้ว​ยังไง​ต่อ? จะ​เอา​กำแพง Post-it นี้​ไป​ทำ​เป็น design และ code ที่​รัน​ได้​อย่างไร โดย​ไม่​ทำให้​ความ​รู้​ที่​อุตส่าห์​ขุด​มา​หล่น​หาย​ระหว่าง​ทาง คำ​ตอบสั้นๆ คือ Event Storming ไม่ใช่​จุดจบ แต่​เป็น สะพาน จาก​เวิร์กช็อป​ไป​สู่ Bounded ContextBounded Contextขอบเขต​ที่ model และ ubiquitous language หนึ่ง​ใช้ได้​และ​มี​ความหมาย​แน่นอน ใน EventStorming มัก​ค้น​พบ​ขอบเขต​นี้​จาก pivotal event และ​กลุ่ม event ที่​เกาะ​กลุ่ม​กันStrategic Design และ code แบบ DDD

ย้อน​กลับ​ไป​ที่ Pivotal EventPivotal Eventdomain event สำคัญ​ที่​แบ่ง​ไทม์​ไลน์​ออก​เป็น​ช่วง ๆ (เช่น OrderPlaced, FoodDelivered) มัก​ขีด​เส้น​แนว​ตั้ง​กำกับ ใช้​เป็น​เบาะแส​ของ​ขอบเขต bounded context ใน​ภายหลังProcess — event สำคัญ​ที่​เรา​ขีด​เส้น​แนว​ตั้ง​กำกับ​ไว้ (OrderPlaced, PaymentCaptured, FoodDelivered) มัน​ไม่​ได้​แค่​แบ่ง​ไทม์​ไลน์​ให้​อ่าน​ง่าย​เท่านั้น แต่​มัน​คือ รอยร้าว​ตาม​ธรรมชาติ​ของ domain ลอง​สังเกต​สิ่ง​ที่​เกิด​ขึ้น​รอบๆ เส้น​แนว​ตั้ง​พวก​นี้​ให้​ดี: ก่อน​เส้น​กับ​หลัง​เส้น คน​ที่​คุย​เปลี่ยนหน้า คำ​ศัพท์​ที่​ใช้​เปลี่ยน​ชุด และ​กฎ​ที่​ต้อง​รักษา​ก็​เปลี่ยน​เรื่อง

ก่อน OrderPlaced เรา​พูด​ภาษา​ของ “ตะกร้า” — เพิ่ม​ของ ลบ​ของ คำนวณ​ราคา​รวม หลัง PaymentCaptured เรา​พูด​ภาษา​ของ “การ​จัด​ส่ง” — ใคร​ไป​รับ เส้นทาง​ไหน ถึง​เมื่อไหร่ คำ​ว่า “ออเดอร์” ใน​สอง​ฝั่ง​นี้​ก็​ไม่​เหมือนกันเป๊ะๆ ด้วย​ซ้ำ ฝั่ง​สั่ง​ซื้อ​มองออเดอร์​เป็น “รายการ​อาหาร​กับ​ราคา” ส่วน​ฝั่ง​จัด​ส่ง​มองออเดอร์​เป็น “พัสดุ​ที่​มี​ต้นทาง​ปลายทาง” นี่แหละ​คือ​สัญญาณ​ของ ภาษา​ที่​เปลี่ยน ซึ่ง​เป็น​หัวใจ​ของ Bounded Context

เมื่อ event และ aggregate เกาะ​กลุ่ม​กัน​แน่น​ภายใน​ช่วง​หนึ่ง​ของ​ไทม์​ไลน์ และ​ภาษา​เปลี่ยน​ตรง​รอย​ต่อ นั่น​คือ​เบาะแส​ว่า​เรา​เจอ ขอบเขต​หนึ่ง แล้ว

flowchart LR
  subgraph ORD["Ordering Context"]
    E1["ItemAddedToCart"]
    E2["OrderPlaced"]
  end
  subgraph PAY["Payment Context"]
    E3["PaymentCaptured"]
  end
  subgraph DIS["Dispatch / Delivery Context"]
    E4["OrderAcceptedByRestaurant"]
    E5["FoodPrepared"]
    E6["RiderAssigned"]
    E7["FoodPickedUp"]
    E8["FoodDelivered"]
  end
  subgraph RAT["Ratings Context"]
    E9["OrderRated"]
  end
  E1 --> E2 --> E3 --> E4 --> E5 --> E6 --> E7 --> E8 --> E9
  classDef evt fill:#e8743b,stroke:#b45309,color:#1a1a1f;
  class E1,E2,E3,E4,E5,E6,E7,E8,E9 evt;

สังเกต​ว่า pivotal event (OrderPlaced, PaymentCaptured, FoodDelivered) นั่ง​อยู่​ตรง​รอย​ต่อ​ของ​กล่อง​พอดี มัน​เป็น event ที่ context หนึ่ง “ปล่อย​ของ” ส่ง​ต่อ​ให้ context ถัด​ไป​รับช่วง เส้น​แบ่ง context ไม่​ได้​ถูก​กำหนด​โดย​สถาปนิก​ที่นั่ง​เทวดา แต่ โผล่​ขึ้น​มา​เอง​จาก​ไทม์​ไลน์ ที่​ทั้ง​ทีม​ช่วย​กัน​สร้าง

เอา​เบาะแส​จาก​ไทม์​ไลน์​มา​ตัด​จริง เรา​ได้​สี่​ขอบเขต​ที่​แต่ละ​อัน​มี​ภาษา​และ​ความ​รับผิดชอบ​ของ​ตัวเอง:

  • Ordering — โลก​ของ​ตะกร้า​และ​การ​ยืนยัน​คำ​สั่ง​ซื้อ พูด​ภาษา Cart, OrderLine, PlaceOrder จบ​หน้าที่​ตรง​ที่​ออเดอร์​ถูกวาง​แล้ว
  • Payment — โลก​ของ​เงิน พูด​ภาษา Charge, Refund, PaymentCaptured สนใจ​แค่​ว่า​เก็บ​เงิน​สำเร็จ​หรือ​ไม่ ไม่​แคร์​ว่า​ใน​กล่อง​มี​ผัด​กะเพรา​หรือ​ข้าว​มัน​ไก่
  • Dispatch / Delivery — โลก​ของ​การ​เคลื่อน​ของ พูด​ภาษา Rider, Route, Pickup, Dropoff มองออเดอร์​เป็น​งาน​ขนส่ง​ชิ้น​หนึ่ง
  • Ratings — โลก​ของ​ฟีด​แบ็ก พูด​ภาษา Review, Score, Comment เกิด​ขึ้น​หลัง​จบ​งาน​ทุก​อย่าง​แล้ว

คำ​ว่า “ออเดอร์” ปรากฏ​ใน​ทั้ง​สี่​ที่ แต่​ไม่​ได้​แปล​ว่า​มัน​ต้อง​เป็น class เดียวกัน — ตรง​กัน​ข้าม การ​พยายาม​ยัด​ออเดอร์​เดียว​ให้​ทุก context ใช้​ร่วม​กัน​คือ​ทาง​ลัด​สู่​ก้อน​โคลน​ที่​พัน​กัน​ยุ่ง แต่ละ context ควร​มี Order version ของ​ตัวเอง​ที่​ถือ​เฉพาะ​ข้อมูล​และ​กฎ​ที่ context นั้น​สนใจ นี่​คือ​หลัก Bounded Context ตรงๆ ที่​คอร์ส DDD สอน แต่​คราว​นี้​เรา ค้น​พบ​มัน​จาก​ผนัง Post-it แทนที่​จะ​นั่ง​เดา​บน​กระดาษ​เปล่า

🍔 อ่าน​ผนัง​เป็น​ขอบเขต

ใน​เวิร์กช็อปฟู้ด​เดลิ​เวอรี ให้​จับ​กลุ่ม event ที่ “พูด​เรื่อง​เดียวกัน” ไว้​ด้วย​กัน แล้ว​มอง​หา​จุด​ที่​คำ​ศัพท์​เปลี่ยน — ItemAddedToCart กับ OrderPlaced อยู่​ก๊วน​เดียวกัน (Ordering) พอ​ถึง PaymentCaptured ภาษา​เปลี่ยน​เป็น​เรื่อง​เงิน (Payment) พอ​ถึง RiderAssigned, FoodPickedUp ภาษา​เปลี่ยน​เป็น​เรื่อง​ขนส่ง (Dispatch) แต่ละ​ก๊วน​ที่​ภาษา​ต่าง​กัน​คือ Bounded Context หนึ่ง​อัน และ Ubiquitous LanguageUbiquitous Languageภาษา​กลาง​ที่​ทีม​และ​ผู้เชี่ยวชาญ​ธุรกิจ​ใช้​ร่วม​กัน EventStorming เป็น​เครื่องมือ​ชั้น​ดี​ใน​การ “ขุด” ภาษา​นี้​ออก​มา เพราะ​ทุก​คำ​บน Post-it ถูก​เถียง​และ​ตกลง​กัน​สด ๆStrategic Design ของ​แต่ละ​อัน​ก็​คือ​คำ​บน Post-it ใน​ก๊วน​นั้น​เอง

รู้​ว่า​มี4 context แล้ว​ยัง​ไม่​พอ ต้อง​รู้​ด้วย​ว่า​มัน คุย​กัน​อย่างไร เครื่องมือ​นั้น​ชื่อ context map — แผนที่​ระดับ​สูง​ที่​บอก​ความ​สัมพันธ์​และ​ทิศทางการ​พึ่งพา​ระหว่าง context โดย​ไม่​ลง​รายละเอียด​ข้าง​ใน

flowchart LR
  ORD["Ordering"]
  PAY["Payment"]
  DIS["Dispatch / Delivery"]
  RAT["Ratings"]
  PG["Payment Gateway"]
  ORD -->|"OrderPlaced (integration event)"| PAY
  PAY -->|"PaymentCaptured"| DIS
  DIS -->|"FoodDelivered"| RAT
  PAY <-->|"ACL"| PG
  classDef ctx fill:#fef3c7,stroke:#b45309,color:#1a1a1f;
  classDef ext fill:#f9a8d4,stroke:#9d174d,color:#1a1a1f;
  class ORD,PAY,DIS,RAT ctx;
  class PG ext;

ลูกศร​ระหว่าง​กล่อง​เหลือง (context ของ​เรา) ส่วน​ใหญ่​คือ integration event — event ที่​ข้าม​ขอบเขต​เพื่อ​บอก context ถัด​ไป​ว่า “ตา​คุณ​แล้ว” OrderPlaced วิ่ง​จาก Ordering ไป Payment PaymentCaptured วิ่ง​ต่อ​ไป Dispatch ที่​น่า​สนใจ​คือ​กล่อง​สีชมพู Payment Gateway — มัน​คือ External SystemExternal Systemระบบ​ภายนอก​ที่​เรา​ไม่​ได้​ควบคุม​แต่​ต้อง​คุย​ด้วย (เช่น Payment Gateway, บริการ​แจ้ง​เตือน, แผนที่) แทน​ด้วย Post-it สีชมพู ช่วย​ชี้​จุด​เชื่อม​ต่อ​และ​ความ​เสี่ยงArchitecture ที่​เรา ไม่​ได้​เป็น​เจ้าของ เรา​สั่ง​มัน​ไม่​ได้ เปลี่ยน API มัน​ไม่​ได้ และ​มัน​ก็​พูด​ภาษา​ของ​มัน​เอง (transaction_id, auth_code, สารพัด field ที่​เรา​ไม่​ได้​ตั้ง​ชื่อ)

ตรง​รอย​ต่อ​กับ​ระบบ​ภายนอก​นี่แหละ​ที่​ต้อง​มี ACL (Anti-Corruption Layer) — ชั้น​แปล​ภาษา​ที่​กัน​ไม่​ให้​ศัพท์​และ​โครงสร้าง​ของ​ระบบ​ภายนอก​ไหล​เข้า​มา​ปน​เปื้อน model ข้าง​ใน Payment context ของ​เรา ACL รับ response ดิบๆ จาก gateway แล้ว​แปล​เป็น PaymentCaptured ที่​พูด​ภาษา​ของ​เรา ถ้า​วัน​หนึ่ง​เรา​เปลี่ยน​เจ้า gateway code ที่​ต้อง​แก้​ก็​จำกัด​อยู่​แค่​ใน ACL ไม่​ลาม​เข้า domain นี่​คือ​เหตุผล​ที่ Hotspot แดงๆ อย่าง “ยกเลิก​หลัง​จ่าย​เงิน​แล้ว คืน​เงิน​ยังไง?” มัก​ซ่อน​ความ​ซับซ้อน​ของ ACL เอา​ไว้ — เพราะ​การ​คืน​เงิน​ต้อง​เต้น​ตาม​จังหวะ​ของ​ระบบ​ภายนอก ไม่ใช่​ของ​เรา

External System บน​ผนัง = จุด​ที่​ต้อง​มี ACL

ทุก​ครั้ง​ที่​เจอ Post-it สีชมพู (External System) ติด​กับ​รอย​ต่อ context ให้​ตีวง​ตรง​นั้น​ว่า​เป็น​ตำแหน่ง​ของ ACL ใน code Payment Gateway, Push Notification, Map/Routing ล้วน​ต้อง​มี​ชั้น​แปล​ของ​ตัวเอง มัน​ไม่ใช่​งาน​เสริม — มัน​คือ​ปราการ​ที่​ทำให้ Ubiquitous Language ภายใน context สะอาด​อยู่​ได้​ทั้ง​ที่​ต้อง​คุย​กับ​โลก​ภายนอก​ที่​เรา​คุม​ไม่​ได้

ทีนี้​ซูม​เข้าไป​ใน1 context แล้ว​แปลง Post-it เป็น class กฎ​การ​แปล​จำ​ง่าย​มาก:

  • Aggregate (เหลือง​ใหญ่) → class ที่​ถือ invariant ของ​ตัวเอง
  • Command (ฟ้า) → method บน aggregate นั้น
  • Domain Event (ส้ม) → สิ่ง​ที่ method นั้น publish ออก​มา หลัง​จาก​กฎ​ผ่าน​แล้ว
flowchart LR
  A["ลูกค้า (Actor)"] --> C["PlaceOrder (Command)"]
  C --> AG["Order (Aggregate)"]
  AG --> E["OrderPlaced (Domain Event)"]
  E --> P["Policy: whenever PaymentCaptured แล้วสั่ง AcceptOrder"]
  classDef actor fill:#fde047,stroke:#a16207,color:#1a1a1f;
  classDef cmd fill:#3b82f6,stroke:#1e40af,color:#f8fafc;
  classDef agg fill:#fef3c7,stroke:#b45309,color:#1a1a1f;
  classDef evt fill:#e8743b,stroke:#b45309,color:#1a1a1f;
  classDef pol fill:#c4b5fd,stroke:#6d28d9,color:#1a1a1f;
  class A actor;
  class C cmd;
  class AG agg;
  class E evt;
  class P pol;

เขียน​เป็น code ตรงๆ ก็ได้​หน้าตา​แบบ​นี้ หัวใจ​อยู่​ตรง​ที่ กฎ​อยู่​ข้าง​ใน class และ command เป็น​ทาง​เข้า​เดียว​ที่​จะ​เปลี่ยน​สถานะ​ได้:

Order.ts — aggregate ที่​ถือ invariant
class Order {
private items: OrderLine[] = [];
private status: OrderStatus = "Draft";
// Command = method: ตรวจกฎก่อน แล้วค่อยเปลี่ยนสถานะ
placeOrder(): OrderPlaced {
if (this.items.length === 0) {
throw new Error("สั่งอาหารโดยตะกร้าว่างไม่ได้");
}
this.status = "Placed";
// Event = สิ่งที่ publish ออกไปหลังกฎผ่าน
return new OrderPlaced(this.id, this.items);
}
}

กฎ “ตะกร้า​ว่าง​สั่ง​ไม่​ได้” คือ invariant — สิ่ง​ที่​ต้อง​จริง​เสมอ​ไม่​ว่า​ใคร​จะ​ทำ​อะไร​กับ Order และ​เพราะ​มัน​อยู่​ใน placeOrder() จึง​ไม่มี​ทาง​ที่​ใคร​จะ​ข้าม​มัน​ไป​ได้ class นี้​ปกป้อง​ความ​ถูกต้อง​ของ​ตัวเอง นี่​คือ​ความหมาย​ของ “หน่วย​ความ​สอดคล้อง (consistency boundary)” ที่​คอร์ส DDD พูด​ถึง แต่​คราว​นี้​เรา​ได้​มัน​มา​จาก Post-it เหลือง​ใหญ่​บน​ผนัง

กับดัก: Anemic Domain Model

กับดัก​คลาสสิก​คือ​เปลี่ยน Aggregate เป็น class ที่​มี​แต่ getter/setter ไม่มี​กฎ​อยู่​ข้าง​ใน แล้ว​ผลัก logic ทั้งหมด​ไป​ไว้​ที่ service ข้าง​นอก แบบ​นี้:

อย่า​ทำ — anemic
class Order {
items: OrderLine[] = [];
status: string = "";
}
class OrderService {
place(order: Order) {
order.status = "Placed"; // ใครก็เซ็ตได้ ไม่มีใครกันตะกร้าว่าง
}
}

โครง​แบบ​นี้​เรียก​ว่า anemic domain model — มัน​ดูเหมือน object แต่​ข้าง​ใน​กลวง กฎ (invariant) รั่ว​ออก​ไป​กอง​ที่ service ทำให้​ไม่มี​ใคร​เป็น​เจ้าของ​ความ​ถูกต้อง​ของ Order อีก​ต่อ​ไป ผล​ของ Event Storming ที่​ชัดเจน (aggregate ตัว​ไหน​ปล่อย event อะไร ใต้​กฎ​อะไร) คือ​ยา​แก้​กับดัก​นี้​โดยตรง เพราะ​มัน​บอก​เรา​ตั้งแต่​ต้น​ว่า กฎ​อะไร​ต้อง​อยู่​ใน class ไหน

Post-it สี​ม่วง — Policy แบบ “whenever event แล้ว​สั่ง command” — แปลง​เป็น code ได้​สอง​รูปแบบ ขึ้น​กับ​ว่า​มัน​ข้าม​ขอบเขต​หรือ​ไม่

ถ้า event กับ command ที่​มัน​เชื่อม อยู่​ใน context เดียวกัน policy ก็​เป็น​แค่ handler เล็กๆ ที่ subscribe event แล้ว​ยิง command ต่อ แต่​ถ้า policy ต้อง ประสาน​งาน​ข้าม aggregate หรือ​ข้าม context และ​มี​สถานะ​ที่​ต้อง​จำ​ระหว่าง​ขั้นตอน (เช่น “รอ PaymentCaptured ภายใน 5 นาที ถ้า​ไม่​มา​ให้​ยกเลิก​ออเดอร์”) มัน​จะ​โต​ขึ้น​เป็น process manager หรือ saga — object ที่​คอย​ติดตาม​ว่า​กระบวนการ​ยาวๆ เดิน​ไป​ถึง​ไหน​แล้ว และ​ตัดสิน​ใจ step ถัด​ไป

จุด​สำคัญ​คือ event ที่ policy ฝั่ง​หนึ่ง​ฟัง อาจ​ถูก​ปล่อย​จาก​อีก context หนึ่ง — เมื่อ event เดิน​ทางข้าม​เส้น Bounded Context เรา​เรียก​มัน​ว่า integration event (ต่าง​จาก domain event ภายใน​ที่​ไม่​เคย​ออก​นอก​ขอบเขต) ในฟู้ด​เดลิ​เวอรี policy “whenever PaymentCaptured แล้ว​แจ้ง​ร้าน​ให้​กด​รับ” คือ integration event ที่ Payment ปล่อย​ให้ Dispatch ฟัง ส่วน “whenever OrderAcceptedByRestaurant แล้ว​หา​ไร​เด​อร์” เป็น policy ภายใน Dispatch เอง การ​แยก​สอง​ชนิด​นี้​ให้​ชัด​ตั้งแต่​บน​ผนัง ช่วย​ให้​ตอน​ออกแบบ integration รู้​ว่า event ไหน​คือ “สัญญา​สาธารณะ” ที่​ห้าม​เปลี่ยนพล่อยๆ

ถอย​ออก​มา​มอง​ภาพ​รวม จะ​เห็น​ว่า​ทุก​อย่าง​คือ​การ​เดิน​กลับ​เข้า​สู่​คอร์ส DDD แบบ​มี​ของ​ติดมือ:

  • ผล​ฝั่ง strategic design — pivotal event ให้ Bounded Context, รอย​ต่อ​ระหว่าง​กลุ่ม event ให้ context map และ ACL, คำบน Post-it ให้ Ubiquitous Language ที่​ทุก​คน​เถียง​กัน​จน​ตกผลึก​แล้ว
  • ผล​ฝั่ง tactical design — aggregate ให้ class ที่​ถือ invariant, command ให้ method, domain event ให้​สิ่ง​ที่ publish, policy ให้ process manager/saga

Event Storming ไม่​ได้​มา​แทน DDD และ​ไม่​ได้​แข่ง​กับ DDD — มัน​คือ สะพาน​ให้​คน​เดิน​ข้าม จาก​ห้อง​เวิร์กช็อป​ที่​เต็ม​ไป​ด้วย​ผู้เชี่ยวชาญ​ธุรกิจ ไป​สู่ model และ code ที่​นัก​พัฒนา​ลงมือ​ทำ ปัญหา​ใหญ่​สุด​ของ DDD ที่​ทำ​จริง​แล้ว​พัง​บ่อยๆ คือ “ขุด domain ไม่​ออก” หรือ “domain expert กับ dev พูด​กัน​คนละ​ภาษา” Event Storming แก้​ตรง​นั้น​ก่อน​ที่​จะ​ไป​แตะ tactical pattern ด้วย​ซ้ำ

ครบ​วงจร: workshop → design → code

ลำดับ​ที่​ใช้ได้​จริง​คือ Big Picture EventStormingBig Picture EventStormingเวิร์กช็อป EventStorming รอบ​แรก​และ​กว้าง​ที่สุด เชิญ​คน​จาก​ทุก​ฝ่าย​มาระดม domain event ของ​ทั้ง​สาย​ธุรกิจ​ลง​บน​ไทม์​ไลน์​เดียว เป้าหมาย​คือ​เห็น​ภาพ​รวม ค้น​พบ​รอย​ต่อ​และ​จุด​ที่​ยัง​ไม่รู้ ไม่​เน้น​รายละเอียดProcess เพื่อ​เห็น​ทั้ง​สาย​ธุรกิจ → Process ModelingProcess Modelingระดับ​กลาง​ของ EventStorming ที่​ต่อ​จาก Big Picture — เพิ่ม command, actor, policy และ read model เข้า​มา​เพื่อ​ให้​เห็น “กลไก” ที่​ทำให้​แต่ละ event เกิด​ขึ้น ก่อน​จะ​ลง​ลึก​ระดับ designProcess เพื่อ​เติม command/policy/read model → Design-Level EventStormingDesign-Level EventStormingEventStorming รอบ​เจาะ​ลึก​เข้าไป​ใน bounded context เดียว เพื่อ​ออกแบบ​ระดับ​ที่​พร้อม​เขียน code โดย​เรียง​ลำดับ command → aggregate → event และ policy อย่าง​ละเอียดProcess เพื่อ​ซูม​เข้า1 context ให้​พร้อม​เขียน code แล้ว​ค่อย​หยิบ tactical pattern ของ DDD มา​ลงมือ ทุก​ก้าว​มี​ของ​จาก​ก้าว​ก่อนหน้า​ส่ง​ต่อ ไม่มี​ช่วง​ไหน​ที่​ต้อง​เริ่ม​จาก​กระดาษ​เปล่า

จบ​คอร์ส Event Storming แล้ว ถ้า​อยาก​ลง​ลึก​ฝั่ง design และ code ต่อ แนะนำ​เดิน​ตาม​นี้:

  • คอร์ส Domain-Driven Design ของ​เว็บ — เจาะ strategic (bounded context, context map, ubiquitous language) และ tactical (aggregate, entity, value object) แบบ​เต็มๆ ต่อยอด​จาก​ผล​เวิร์กช็อป​ที่​เพิ่ง​ได้​มา
  • คอร์ส DDD Distilled — ฉบับ​กระชับ​ของ Vaughn Vernon เหมาะ​ถ้า​อยาก​ได้​ภาพ​รวม DDD เร็วๆ ก่อน​ลง​ลึก
  • คอร์ส Patterns, Principles, and Practices of DDD — ลึก​ที่สุด​ใน​เรื่อง tactical pattern และ​การ​วาง​สถาปัตยกรรม​รอบๆ model

ผนัง Post-it ที่​คุณ​สร้าง​ใน​คอร์ส​นี้​จะ​ไม่​สูญเปล่า — มัน​คือ​แผนที่​ที่​พา​คุณ​เดิน​เข้า​สู่​โลก DDD โดย​ไม่​หลง

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

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

  • Bounded Context — นิยาม​ขอบเขต​ที่ pivotal event ช่วย​ให้​เรา​ค้น​พบ​จาก​ไทม์​ไลน์
  • Context Map — แผนที่​ความ​สัมพันธ์​และ​ทิศทางการ​พึ่งพา​ระหว่าง context ที่​เรา​วาด
  • Aggregate — จาก Post-it เหลือง​ใหญ่​สู่ class ที่​ถือ invariant และ​รับ command เป็น method
  • Anemic Domain Model — กับดัก​ที่​ต้อง​เลี่ยง​ตอน​แปลง aggregate เป็น code
  • Tactical Design — building block ที่​ใช้​ต่อ​จาก​ผล​เวิร์กช็อป​เมื่อ​ลงมือ​เขียน​จริง

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

ข้อ 1 / 3

pivotal event สัมพันธ์กับ bounded context อย่างไร?