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

Bounded Context

ขอบเขต​ภายใน domain ที่​บรรจุ model ของ​บริบท​นั้น​ไว้

Bounded Context คือ ขอบเขต​เชิง​ภาษา​และ​เชิง model (linguistic and model boundary) ที่​ห่อ​หุ้ม ubiquitous language หนึ่ง​ชุด​ไว้ ภายใน​ขอบเขต​นั้น คำ​ศัพท์​ทุก​คำ — ชื่อ entity, value object, aggregate, method, event — มี​ความหมาย​เดียว ไม่​คลุมเครือ และ​สอดคล้อง​กัน​ทั้ง​ใน code เอกสาร diagram และ​บทสนทนา​ของ​ทีม

Eric Evans อธิบาย​ไว้​ใน “Domain-Driven Design” (2003) ว่า​ระบบ​ขนาด​ใหญ่​ไม่​สามารถ​มี model เดียว​ที่ “รวม​เป็น​หนึ่ง” (unified) ครอบคลุม​ทั้ง​องค์กร​ได้​อย่าง​คุ้ม​ค่า เพราะ​แต่ละ​ส่วน​ของ​ธุรกิจ​มัก​ใช้​คำ​เดียวกัน​ใน​ความหมาย​ต่าง​กัน Martin Fowler ยก​ตัวอย่าง​คำ​ว่า “meter” ใน​บริษัท​ไฟฟ้า — ทีม​วัด​ปริมาณ​การ​ใช้​ไฟ​มอง​ว่า meter คือ​อุปกรณ์​วัด แต่​ทีม​บัญชี​มอง​ว่า meter คือ​จุด​อ้างอิง​ใน​การ​เรียก​เก็บ​เงิน ทั้ง​สอง​ความหมาย​ถูกต้อง​ใน​บริบท​ของ​ตัวเอง แต่​จะ​ชน​กัน​ถ้า​พยายาม​ยัด​รวม​ไว้​ใน model เดียว Bounded Context จึง​ทำ​หน้าที่​แยก​ความหมาย​เหล่า​นี้​ออก​จาก​กัน​อย่าง​ชัดเจน แทนที่​จะ​พยายาม​หา “ความ​จริง​หนึ่ง​เดียว” ที่​ใช้ได้​กับ​ทุก​คน

ข้อ​ควร​ระวัง: bounded context ไม่ใช่ แค่ “module” หรือ “layer” ทาง​เทคนิค แต่​คือ​ขอบเขต​ที่​ถูก​กำหนด​โดย​ความหมาย​และ​การ​ใช้​ภาษา​ของ​คนใน​องค์กร — บ่อย​ครั้ง​จึง​ล้อ​ไป​กับ​โครงสร้าง​ทีม​และ​วัฒนธรรม​องค์กร (สัมพันธ์​กับ Conway’s Law)

Bounded Context เป็น​แกน​กลาง​ของ strategic design ใน DDD (คู่​กับ tactical design ที่​ว่าด้วย entity/value object/aggregate ภายใน​บริบท​เดียว) บทบาท​หลัก​มี​สาม​ด้าน

  1. ควบคุม​ความ​สอดคล้อง​ของ​ภาษา — ทีม​งาน​ใน​บริบท​เดียวกัน​พูด​คุย​ด้วย​คำ​ศัพท์​เดียวกัน​โดย​ไม่​ต้อง​แปล​ไป​มา ลด​ความ​เข้าใจ​ผิด​ระหว่าง developer กับ domain expert
  2. กำหนด​ขอบเขต​ความ​รับผิดชอบ​ของ​ทีม​และ codebase — domain หนึ่ง (domain ที่​แตก​เป็น​หลาย subdomain) อาจ​มี​หลาย bounded context และ​แต่ละ​อัน​มัก​ดูแล​โดย​ทีม​ต่าง​กัน เช่น​ระบบ eCommerce อาจ​มี Catalog, Customer, Inventory, Order, Marketing, Fraud Detection เป็น​บริบท​แยก​กัน
  3. เป็น​หน่วย​ของ​การ​วาง​สถาปัตยกรรม — ความ​สัมพันธ์​ระหว่าง bounded context หลาย​อัน​ถูก​อธิบาย​ด้วย Context Mapping ซึ่ง​ระบุ pattern ความ​สัมพันธ์ เช่น Shared Kernel, Customer-Supplier, Conformist, Anti-Corruption Layer (ACL) และ Open Host Service

แนว​ปฏิบัติ​สมัย​ใหม่ (เช่น​แนวทาง​ของ Microsoft Azure Architecture Center สำหรับ microservices) แนะนำ​ว่า ไม่​ควร​ให้ microservice หนึ่ง​ครอบคลุม​มากกว่า1 bounded context และ​ใน​ทาง​กลับ​กัน microservice ที่​เล็ก​เกิน​ไป​มัก​เป็น​เพียง aggregate เดียว​ภายใน bounded context — กล่าว​คือ​ขนาด​ที่​เหมาะสม​ของ microservice ควร​อยู่​ระหว่าง “ไม่​เล็ก​กว่า aggregate” และ “ไม่​ใหญ่​กว่า bounded context” นี่​คือ​เหตุผล​ที่ bounded context มัก​ถูก​ใช้​เป็น​จุด​เริ่มต้น​ใน​การ​ตัด​แบ่ง microservices architecture

ระบบ eCommerce หนึ่ง​ระบบ​อาจ​มี​คำ​ว่า “Product” ปรากฏ​ใน​หลาย​บริบท แต่​แต่ละ​บริบท​ให้​ความหมาย​และ attribute ต่าง​กัน

  • ใน Catalog context — Product คือ​รายการ​ที่​มีชื่อ คำ​อธิบาย รูปภาพ และ​หมวด​หมู่ สำหรับ​ให้​ลูกค้า​ค้นหา​และ​เปรียบเทียบ
  • ใน Inventory context — Product (หรือ​เรียก​ว่า SKU) คือ​หน่วย​ที่​มี​จำนวน​คง​เหลือ​ใน​คลัง ตำแหน่ง​จัด​เก็บ และ​สถานะ​การ​เติม​สินค้า
  • ใน Order context — Product ปรากฏ​เป็น line item ที่​ตรึง​ราคา​และ​ชื่อ ณ เวลา​ที่​สั่ง​ซื้อ​ไว้ (snapshot) เพื่อ​ไม่​ให้​ราคา​ที่​เปลี่ยน​ภายหลัง​กระทบ​ใบสั่ง​ซื้อ​เก่า

ทั้ง​สาม​บริบท​ไม่​ได้​ใช้ class Product ร่วม​กัน แต่ละ​บริบท​มี model ของ​ตัวเอง และ​เชื่อม​ต่อ​กัน​ผ่าน​การ​แปล​ข้อมูล (translation) ที่​ชายแดน​ของ​บริบท ตัวอย่าง code C# ด้าน​ล่าง​แสดง​ว่า Catalog context และ Order context ต่าง​มี​นิยาม Product เป็น​ของ​ตัวเอง โดย​มี translator ทำ​หน้าที่​แปลง​ข้อมูล​ข้าม​ขอบเขต

// ===== Catalog bounded context =====
// Product ที่นี่เน้นข้อมูลสำหรับแสดงผลและค้นหา
namespace Catalog.Domain
{
public class Product
{
public Guid ProductId { get; }
public string Name { get; }
public string Description { get; }
public string Category { get; }
public decimal CurrentPrice { get; private set; }
public Product(Guid productId, string name, string description, string category, decimal currentPrice)
{
ProductId = productId;
Name = name;
Description = description;
Category = category;
CurrentPrice = currentPrice;
}
public void Reprice(decimal newPrice) => CurrentPrice = newPrice;
}
}
// ===== Order bounded context =====
// Product ที่นี่คือ value object ของ line item ตรึงค่า ณ เวลาสั่งซื้อ ไม่อ้างอิงถึง Catalog.Product เลย
namespace Order.Domain
{
public sealed class OrderedProduct
{
public Guid ProductId { get; }
public string NameSnapshot { get; }
public decimal PriceSnapshot { get; }
public OrderedProduct(Guid productId, string nameSnapshot, decimal priceSnapshot)
{
ProductId = productId;
NameSnapshot = nameSnapshot;
PriceSnapshot = priceSnapshot;
}
}
// translator ที่ชายแดนระหว่างสองบริบท แปลง Catalog.Product เป็น OrderedProduct
public static class CatalogToOrderTranslator
{
public static OrderedProduct Translate(Catalog.Domain.Product catalogProduct)
{
// จงใจคัดลอกเฉพาะข้อมูลที่ Order context ต้องใช้ ณ เวลานี้เท่านั้น
return new OrderedProduct(
catalogProduct.ProductId,
catalogProduct.Name,
catalogProduct.CurrentPrice);
}
}
}

แผนภาพ​ต่อ​ไป​นี้​แสดง domain eCommerce ที่​แบ่ง​ออก​เป็น​หลาย bounded context โดย​แต่ละ​อัน​มี ubiquitous language ของ​ตัวเอง และ​เชื่อม​ต่อ​กัน​ผ่าน context mapping

graph TB
    Domain[eCommerce Domain]
    Catalog[Catalog Context]
    Inventory[Inventory Context]
    Order[Order Context]
    Fraud[Fraud Detection Context]
    UL1[Ubiquitous Language A]
    UL2[Ubiquitous Language B]
    UL3[Ubiquitous Language C]

    Domain --> Catalog
    Domain --> Inventory
    Domain --> Order
    Domain --> Fraud
    Catalog --> UL1
    Inventory --> UL2
    Order --> UL3
    Catalog -.->|context mapping| Order
    Order -.->|context mapping| Inventory
    Order -.->|context mapping| Fraud
  • Domain และ Subdomaindomain คือ​พื้นที่​ปัญหา​ทาง​ธุรกิจ​ทั้งหมด ซึ่ง​วิเคราะห์​แตก​เป็น subdomain (core/supporting/generic) ส่วน bounded context คือ​ขอบเขต​ของ solution space — โดย​อุดมคติ​แล้ว bounded context หนึ่ง​ควร​สอดคล้อง​กับ subdomain หนึ่ง แต่​ใน​ทาง​ปฏิบัติ​ความ​สัมพันธ์​อาจ​ไม่​ตรง​กัน​เป๊ะ​เสมอ​ไป (หลาย context ต่อ1 subdomain หรือ​กลับ​กัน)
  • Ubiquitous Language — แต่ละ bounded context มี ubiquitous language เป็น​ของ​ตัวเอง คำ​เดียวกัน​อาจ​มี​ความหมาย​ต่าง​กัน​คนละ​บริบท นี่​คือ​สาเหตุ​หลัก​ที่​ต้อง​มี​ขอบเขต​ตั้งแต่​แรก
  • Context Mapping — เมื่อ​มี​หลาย bounded context ความ​สัมพันธ์​ระหว่าง​กัน​ต้อง​ถูก​อธิบาย​อย่าง​ชัดเจน​ด้วย Context Mapping ผ่าน pattern ต่าง ๆ เช่น Shared Kernel, Anti-Corruption Layer, Open Host Service
  • Anti-Corruption Layer — เมื่อ bounded context ต้อง​เชื่อม​กับ​ระบบ legacy หรือ​บริบท​ที่ model ไม่​สอดคล้อง​กัน มัก​ใช้ ACL เป็น​ชั้น​แปล​ข้อมูล​กัน model ของ​บริบท​ตัวเอง​ไม่​ให้​ถูก “ปน​เปื้อน”
  • Conway’s Law — ขอบเขต​ของ bounded context มัก​สะท้อน​โครงสร้าง​การ​สื่อสาร​ของ​ทีม/องค์กร ตาม Conway’s Law ทีม​ที่​แยก​กัน​มัก​ผลิต​บริบท​ที่​แยก​กัน​ตาม​ไป​ด้วย
  • Microservices — ใน​สถาปัตยกรรม microservices สมัย​ใหม่ bounded context มัก​ถูก​ใช้​เป็น​เกณฑ์​กำหนด​ขอบเขต​ของ service แต่ละ​ตัว เพื่อ​ไม่​ให้ service เดียว​ต้อง​รับผิดชอบ​มากกว่า1 model domain