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

Tactical Design

เมื่อ​รู้​ขอบเขต​แล้ว เรา​ลงมือ​สร้าง Domain ModelDomain ModelModel ของ Domain ที่​ฝัง​กฎ​และ​กระบวนการ​ทาง​ธุรกิจ​ไว้​ใน code จริง หัวใจ​ของ DDD คือ​การ​พัฒนา domain model ที่ “เข้าใจ​ธุรกิจ​อย่าง​ลึกซึ้ง”Strategic Design ที่ “รวย​พฤติกรรม” ด้วย building blocks ของ DDD Fowler จัด​กลุ่ม​หลัก​ไว้​สาม​อย่าง (Evans Classification): EntityEntityอ็อบเจ็กต์ที่​มี “ตัวตน” (identity) ต่อ​เนื่อง​ตาม​เวลา แม้​ค่า​ภายใน​เปลี่ยน ก็​ยัง​เป็น​สิ่ง​เดิม เช่น ลูกค้า, คำ​สั่ง​ซื้อ — แยกแยะ​ด้วย id ไม่ใช่​ด้วย​ค่าTactical Design, Value ObjectValue Objectอ็อบเจ็กต์ที่​สำคัญ​ที่ “ค่า” ของ​มัน ไม่มี​ตัวตน​เฉพาะ สอง​ตัว​ที่​ค่า​เท่า​กัน​ถือว่า​เท่า​กัน ควร​เป็น immutable (เปลี่ยน​ค่า​ไม่​ได้) เช่น เงิน, วัน​ที่, พิกัดTactical Design, และ service

EntityEntityอ็อบเจ็กต์ที่​มี “ตัวตน” (identity) ต่อ​เนื่อง​ตาม​เวลา แม้​ค่า​ภายใน​เปลี่ยน ก็​ยัง​เป็น​สิ่ง​เดิม เช่น ลูกค้า, คำ​สั่ง​ซื้อ — แยกแยะ​ด้วย id ไม่ใช่​ด้วย​ค่าTactical Design คือ​อ็อบเจ็กต์ที่​มี IdentityIdentity“ตัวตน” ที่​ทำให้ Entity เป็น​สิ่ง​เดิม​ตลอด​อายุ แม้​ค่า​เปลี่ยน เช่น เลข​ที่​คำ​สั่ง​ซื้อ หรือ id ที่​ระบบ​สร้าง​ให้Tactical Design ต่อ​เนื่อง​ตาม​เวลา แม้​ค่า​ภายใน​เปลี่ยน ก็​ยัง​เป็น “สิ่ง​เดิม” เช่น ลูกค้า​คน​หนึ่ง​เปลี่ยน​ชื่อ–ที่​อยู่​ได้ แต่​ก็​ยัง​เป็น​ลูกค้า​คน​เดิม เรา​แยกแยะ​ด้วย id ไม่ใช่​ด้วย​ค่า และ​ที่​สำคัญ Entity ต้อง “มี​พฤติกรรม” ไม่ใช่​แค่​เก็บ​ข้อมูล

Value ObjectValue Objectอ็อบเจ็กต์ที่​สำคัญ​ที่ “ค่า” ของ​มัน ไม่มี​ตัวตน​เฉพาะ สอง​ตัว​ที่​ค่า​เท่า​กัน​ถือว่า​เท่า​กัน ควร​เป็น immutable (เปลี่ยน​ค่า​ไม่​ได้) เช่น เงิน, วัน​ที่, พิกัดTactical Design คือ​อ็อบเจ็กต์ที่​เรา​สนใจ​แค่ “ค่า” ของ​มัน ไม่มี​ตัวตน​เฉพาะ สอง​ตัว​ที่​ค่า​เท่า​กัน​ถือว่า​เท่า​กัน เช่น เงิน 100 บาท ก็​คือ 100 บาท ไม่​ว่า​จะ​เป็น​วัตถุ​ไหน กฎ​สำคัญ​คือ​ควร​เป็น ImmutableImmutableเปลี่ยนแปลง​ไม่​ได้​หลัง​สร้าง ถ้า​จะ “เปลี่ยน​ค่า” ต้อง​สร้าง​อ็อบเจ็กต์ใหม่​แทน หลักการ​สำคัญ​ของ Value Object เพื่อ​เลี่ยง bug จาก​การ​ใช้​อ้างอิง​ร่วม​กันTactical Design — ถ้า​จะ​เปลี่ยน​ค่า ให้​สร้าง​ตัว​ใหม่​แทน เพื่อ​เลี่ยง bug จาก​การ​ใช้​อ้างอิง​ร่วม​กัน

Value Object ที่​ดี​จะ​รวม validation ไว้​ใน​ตัว ทำให้ “ค่าที่​ไม่​ถูกต้อง” เป็น​ไป​ไม่​ได้​ตั้งแต่​แรก
// Value Object: เทียบกันด้วยค่า, เปลี่ยนแปลงไม่ได้, validate ตอนสร้าง
class Money {
private constructor(
readonly amountSatang: number, // เก็บเป็นสตางค์ (เลขจำนวนเต็ม) เลี่ยงปัญหาทศนิยม
readonly currency: string,
) {}
static of(amountSatang: number, currency: string): Money {
if (!Number.isInteger(amountSatang)) throw new Error('ต้องเป็นจำนวนเต็ม');
if (amountSatang < 0) throw new Error('เงินติดลบไม่ได้');
return new Money(amountSatang, currency);
}
add(other: Money): Money {
if (other.currency !== this.currency) throw new Error('สกุลเงินไม่ตรงกัน');
return Money.of(this.amountSatang + other.amountSatang, this.currency); // คืนตัวใหม่
}
equals(other: Money): boolean { // เท่ากันเมื่อค่าทุกตัวเท่ากัน
return this.amountSatang === other.amountSatang && this.currency === other.currency;
}
}

AggregateAggregateกลุ่ม​ของ Entity และ Value Object ที่​ถูก​มอง​เป็น​หนึ่ง​หน่วย​เดียว​เพื่อ​รักษา​ความ​ถูกต้อง​ของ​ข้อมูล มี​ขอบเขต​ชัดเจน และ​เป็น​หน่วย​ของ transactionTactical Design คือ​กลุ่ม​ของ EntityEntityอ็อบเจ็กต์ที่​มี “ตัวตน” (identity) ต่อ​เนื่อง​ตาม​เวลา แม้​ค่า​ภายใน​เปลี่ยน ก็​ยัง​เป็น​สิ่ง​เดิม เช่น ลูกค้า, คำ​สั่ง​ซื้อ — แยกแยะ​ด้วย id ไม่ใช่​ด้วย​ค่าTactical Design และ Value ObjectValue Objectอ็อบเจ็กต์ที่​สำคัญ​ที่ “ค่า” ของ​มัน ไม่มี​ตัวตน​เฉพาะ สอง​ตัว​ที่​ค่า​เท่า​กัน​ถือว่า​เท่า​กัน ควร​เป็น immutable (เปลี่ยน​ค่า​ไม่​ได้) เช่น เงิน, วัน​ที่, พิกัดTactical Design ที่​ถูก​มอง​เป็น “หนึ่ง​หน่วย​เดียว” เพื่อ​รักษา​ความ​ถูกต้อง​ของ​ข้อมูล มี Aggregate RootAggregate RootEntity หนึ่ง​ตัว​ที่​เป็น “ประตู​เดียว” เข้า​สู่ Aggregate การ​อ้างอิง​จาก​ภายนอก​ทำได้​กับ root เท่านั้น root จึง​ควบคุม​กฎ​ความ​ถูกต้อง​ของ​ทั้ง​กลุ่มTactical Design เป็น​ประตู​เดียว​เข้า​สู่​กลุ่ม การ​อ้างอิง​จาก​ภายนอก​ทำได้​กับ root เท่านั้น Fowler ย้ำ​กฎ​สำคัญ: “transaction ไม่​ควร​ข้าม​ขอบเขต​ของ Aggregate”

Cargo Aggregate — ขอบเขตความถูกต้อง

aggregate
Cargo Aggregate✓ consistent ภายใน 1 transactionCargoAggregate RootRouteSpecificationValue ObjectItineraryValue ObjectDeliveryValue ObjectHandlingEvent — Aggregate แยกHandlingEventAggregate RootLOAD · UNLOAD · CLAIM …by idTracking ID
Aggregate RootValue Objectขอบเขต (boundary)
กฎข้อ 1 & 2: สิ่งที่อยู่ ภายใน ขอบเขต Cargo ต้องถูกต้องตาม invariant พร้อมกันภายใน หนึ่ง transaction — และควรออกแบบให้เล็กที่สุด
ไดอะแกรม Aggregate ของ Cargo — ขอบเขตความถูกต้อง (invariant) ภายในหนึ่ง transaction
สี่​กฎ​การ​ออกแบบ Aggregate (Vaughn Vernon)

1. จำลอง InvariantInvariantกฎ​ทาง​ธุรกิจ​ที่ “ต้อง​เป็น​จริง​เสมอ” ภายใน Aggregate เช่น ยอด​รวม​คำ​สั่ง​ซื้อ​ต้อง​เท่ากับ​ผล​รวม​ของ​รายการ​สินค้า — ต้อง​ถูกต้อง​ภายใน transaction เดียวTactical Design ที่แท้​จริง​ไว้​ใน​ขอบเขต​ความ​ถูกต้อง — กฎ​ที่​ต้อง​ตรง​กัน​ทันที​ต้อง​อยู่​ใน Aggregate เดียวกัน​และ​ถูกต้อง​ภายใน transaction เดียว

2. ออกแบบ Aggregate ให้​เล็ก — จำกัด​ให้​เหลือ root กับ​สิ่ง​จำเป็น​น้อย​ที่สุด Aggregate ใหญ่​จะ “ไม่มี​วัน​เร็ว​หรือ​สเกล​ได้​ดี”

3. อ้างอิง Aggregate อื่น​ด้วย IdentityIdentity“ตัวตน” ที่​ทำให้ Entity เป็น​สิ่ง​เดิม​ตลอด​อายุ แม้​ค่า​เปลี่ยน เช่น เลข​ที่​คำ​สั่ง​ซื้อ หรือ id ที่​ระบบ​สร้าง​ให้Tactical Design — เก็บ​แค่ id ของ​อีก Aggregate ไม่ใช่​อ้างอิง​ตรง ป้องกัน​การ​แก้​หลาย Aggregate ใน transaction เดียว และ map เข้า​กับ​ขอบเขต microservice ได้​ดี

4. ใช้ Eventual ConsistencyEventual Consistencyความ​ถูกต้อง​ที่ “ตาม​มา​ทีหลัง” ไม่ใช่​ทันที ใช้​กับ​การ​อัปเดต​ข้าม Aggregate ผ่าน Domain Event แทน​การ​บังคับ​ให้​ตรง​กัน​ใน​ทันที​ภายใน transaction เดียวTactical Design นอก​ขอบเขต — เมื่อ​กระบวนการ​ธุรกิจ​กระทบ​หลาย Aggregate ให้​ส่ง Domain EventDomain Eventสิ่ง​ที่ “เกิด​ขึ้น​แล้ว” ใน domain ซึ่ง​ส่วน​อื่น​สนใจ แทน​ด้วย​อ็อบเจ็กต์ที่​เปลี่ยนแปลง​ไม่​ได้ ตั้ง​ชื่อ​เป็น​อดีต เช่น CargoWasRouted ใช้​สื่อสาร​ข้าม AggregateTactical Design แล้ว​ประมวล​ผล​ตาม​มา​แบบ async

ภายนอก​แก้ไข​รายการ​สินค้า​ได้​ผ่าน Order เท่านั้น — root จึง​รับประกัน invariant ได้​เสมอ
// Order = Aggregate Root; OrderLine อยู่ภายในขอบเขต
class Order { // <- Aggregate Root
private lines: OrderLine[] = [];
private constructor(readonly id: OrderId, private status: OrderStatus) {}
// พฤติกรรมอยู่บน root และคอย “บังคับ invariant”
addLine(product: ProductId, qty: number, price: Money): void {
if (this.status !== 'DRAFT') throw new Error('แก้คำสั่งซื้อที่ยืนยันแล้วไม่ได้');
this.lines.push(new OrderLine(product, qty, price));
}
total(): Money { // invariant: ยอดรวม = ผลรวมของรายการ
return this.lines.reduce((sum, l) => sum.add(l.subtotal()), Money.of(0, 'THB'));
}
confirm(): DomainEvent {
if (this.lines.length === 0) throw new Error('คำสั่งซื้อว่างเปล่ายืนยันไม่ได้');
this.status = 'CONFIRMED';
return new OrderConfirmed(this.id, this.total()); // ส่ง event ออกไป (กฎข้อ 4)
}
}
class OrderLine { // ภายใน Aggregate — ไม่มีใครอ้างจากนอกได้
constructor(readonly product: ProductId, readonly qty: number, readonly price: Money) {}
subtotal(): Money { return this.price.times(this.qty); }
}
🚢 ทำไม Handling Event จึง​เป็น Aggregate แยก?

ใน​ระบบ​ขนส่ง Cargo เป็น Aggregate RootAggregate RootEntity หนึ่ง​ตัว​ที่​เป็น “ประตู​เดียว” เข้า​สู่ Aggregate การ​อ้างอิง​จาก​ภายนอก​ทำได้​กับ root เท่านั้น root จึง​ควบคุม​กฎ​ความ​ถูกต้อง​ของ​ทั้ง​กลุ่มTactical Design (มี Route SpecificationRoute SpecificationValue Object ที่​ระบุ “ความ​ต้องการ​ของ​ลูกค้า”: ต้นทาง ปลายทาง และ​กำหนด​เวลา​ถึง — มี method isSatisfiedBy() ตรวจ​ว่า​แผน​เดินทาง​ตรง​ตาม​นี้​หรือ​ไม่Tactical Design, ItineraryItineraryValue Object ที่​เป็น “แผนการ​เดินทาง​จริง” ของ Cargo ประกอบ​ด้วย​ลำดับ​ของ Leg (ช่วง​การ​ขนส่ง​แต่ละ​ช่วง)Tactical Design, Delivery อยู่​ภายใน) ส่วน Handling EventHandling Eventการ​บันทึก​การ​จัดการ​สินค้า​จริง (LOAD, UNLOAD, RECEIVE, CLAIM) เป็น Aggregate “แยก​ต่างหาก” จาก Cargo เพราะ​มี​ปริมาณ​มาก​และ​ต้อง​ประมวล​ผล​แบบ asyncTactical Design — การ​บันทึก​ว่า​สินค้า​ถูก​ยก/ลง/รับ ณ ท่า​ใด — เป็น Aggregate แยก​ต่างหาก

เหตุผล​คือ “ประสิทธิภาพ”: Handling Event หลั่งไหล​เข้า​มา​ปริมาณ​มหาศาล​จาก​ระบบ​ท่าเรือ/คลัง​ภายนอก และ​ต้อง​ประมวล​ผล​เร็ว​แบบ async จึง​ไม่​ควร​โหลด​มา​พร้อม​โครงสร้าง Cargo ที่​ใหญ่ — ตรง​กับ​กฎ “ออกแบบ​ให้​เล็ก” และ “อ้างอิง​ด้วย id” พอดี


RepositoryRepositoryตัว​ที่​ทำให้​เรา​เข้าถึง Aggregate ราวกับ​เป็น collection ใน​หน่วย​ความ​จำ ซ่อน​รายละเอียด​ฐาน​ข้อมูล มี1 repository ต่อ1 Aggregate RootTactical Design ทำให้​เรา​เข้าถึง AggregateAggregateกลุ่ม​ของ Entity และ Value Object ที่​ถูก​มอง​เป็น​หนึ่ง​หน่วย​เดียว​เพื่อ​รักษา​ความ​ถูกต้อง​ของ​ข้อมูล มี​ขอบเขต​ชัดเจน และ​เป็น​หน่วย​ของ transactionTactical Design ราวกับ​เป็น collection ใน​หน่วย​ความ​จำ โดย​ซ่อน​รายละเอียด​ฐาน​ข้อมูล หลักการ​คือ 1 repository ต่อ​หนึ่ง Aggregate RootAggregate RootEntity หนึ่ง​ตัว​ที่​เป็น “ประตู​เดียว” เข้า​สู่ Aggregate การ​อ้างอิง​จาก​ภายนอก​ทำได้​กับ root เท่านั้น root จึง​ควบคุม​กฎ​ความ​ถูกต้อง​ของ​ทั้ง​กลุ่มTactical Design และ interface ควร​นิยาม​ไว้​ใน​ชั้น domain ส่วน implementation อยู่​ชั้น infrastructure เพื่อ​ไม่​ให้ ORM มา “ปน​เปื้อน” model (เรา​จะ​เห็น​เหตุผล​ชัด​ขึ้น​ใน​เรื่อง​สถาปัตยกรรม)

domain ขึ้น​กับ “interface” ที่​ตัวเอง​นิยาม ไม่​ขึ้น​กับ​ฐาน​ข้อมูล — นี่​คือ Port ใน​สถาปัตยกรรม Hexagonal
// interface อยู่ในชั้น domain — domain บอกแค่ว่า “ต้องการอะไร”
interface OrderRepository {
findById(id: OrderId): Promise<Order | null>;
save(order: Order): Promise<void>;
nextId(): OrderId;
}
// implementation จริง (เช่น Drizzle/Postgres) อยู่ชั้น infrastructure แยกต่างหาก

เมื่อ​ตรรกะ​ธุรกิจ​สำคัญ “ไม่​เข้า” กับ Entity หรือ Value Object ตัว​ใด​ตัว​หนึ่ง​โดย​ธรรมชาติ ให้​ทำ​เป็น Domain ServiceDomain Serviceบริการ​ที่​ถือ “ตรรกะ​ธุรกิจ” ซึ่ง​ไม่​เข้า​กับ Entity หรือ Value Object ตัว​ใด​ตัว​หนึ่ง​โดย​ธรรมชาติ เช่น การ​โอน​เงิน​ที่​เกี่ยวข้อง​กับ​สอง​บัญชี — ไร้​สถานะ (stateless)Tactical Design เช่น การ​หา​เส้นทาง หรือ​การ​โอน​เงิน​ที่​กระทบ​สอง​บัญชี ส่วน Application ServiceApplication Serviceบริการ​ที่ “ประสาน​งาน” use case — จัดการ transaction, ความ​ปลอดภัย, การ​บันทึก, แปลง DTO — แต่​ไม่มี​กฎ​ธุรกิจ​อยู่​ใน​ตัว​มัน​เองTactical Design ทำ​หน้าที่ “ประสาน​งาน” use case — เปิด transaction, จัดการ​สิทธิ์, เรียก repository — แต่ ไม่มี​กฎ​ธุรกิจ​ใน​ตัวเอง

เส้น​แบ่ง​ที่​จำ​ง่าย

Domain ServiceDomain Serviceบริการ​ที่​ถือ “ตรรกะ​ธุรกิจ” ซึ่ง​ไม่​เข้า​กับ Entity หรือ Value Object ตัว​ใด​ตัว​หนึ่ง​โดย​ธรรมชาติ เช่น การ​โอน​เงิน​ที่​เกี่ยวข้อง​กับ​สอง​บัญชี — ไร้​สถานะ (stateless)Tactical Design = ถือ “การ​ตัดสิน​ใจ​ทาง​ธุรกิจ” · Application ServiceApplication Serviceบริการ​ที่ “ประสาน​งาน” use case — จัดการ transaction, ความ​ปลอดภัย, การ​บันทึก, แปลง DTO — แต่​ไม่มี​กฎ​ธุรกิจ​อยู่​ใน​ตัว​มัน​เองTactical Design = ประสาน​งานการ​ตัดสิน​ใจ​เหล่า​นั้น (เหมือน​ที่​มัน​ประสาน​งานการ​เรียก method บน Entity) ถ้า Application Service เริ่ม​มี​กฎ​ธุรกิจ นั่น​คือ​สัญญาณ​ว่า​กำลัง​จะ​กลาย​เป็น Anemic Domain ModelAnemic Domain Modelanti-pattern ที่อ็อบเจ็กต์มี​แต่ getter/setter ไร้​พฤติกรรม ตรรกะ​ถูก​ดึง​ไป​ไว้​ใน service ทั้งหมด — จ่าย​ค่า domain model แต่​ไม่​ได้​ประโยชน์​ของ​มัน​เลยTactical Design

Domain EventDomain Eventสิ่ง​ที่ “เกิด​ขึ้น​แล้ว” ใน domain ซึ่ง​ส่วน​อื่น​สนใจ แทน​ด้วย​อ็อบเจ็กต์ที่​เปลี่ยนแปลง​ไม่​ได้ ตั้ง​ชื่อ​เป็น​อดีต เช่น CargoWasRouted ใช้​สื่อสาร​ข้าม AggregateTactical Design แทน “สิ่ง​ที่​เกิด​ขึ้น​แล้ว” ใน domain ที่​ส่วน​อื่น​สนใจ ตั้ง​ชื่อ​เป็น​อดีต​เสมอ (เช่น CargoWasRouted, OrderConfirmed) เป็น ImmutableImmutableเปลี่ยนแปลง​ไม่​ได้​หลัง​สร้าง ถ้า​จะ “เปลี่ยน​ค่า” ต้อง​สร้าง​อ็อบเจ็กต์ใหม่​แทน หลักการ​สำคัญ​ของ Value Object เพื่อ​เลี่ยง bug จาก​การ​ใช้​อ้างอิง​ร่วม​กันTactical Design และ​มัก​พก timestamp กับ id ของ​สิ่ง​ที่​เกี่ยวข้อง ใช้​เพื่อ​ทำ side-effect ข้าม Aggregate และ​เปิด​ทาง Eventual ConsistencyEventual Consistencyความ​ถูกต้อง​ที่ “ตาม​มา​ทีหลัง” ไม่ใช่​ทันที ใช้​กับ​การ​อัปเดต​ข้าม Aggregate ผ่าน Domain Event แทน​การ​บังคับ​ให้​ตรง​กัน​ใน​ทันที​ภายใน transaction เดียวTactical Design ส่วน FactoryFactoryตัว​ที่​ห่อ​หุ้ม​ตรรกะ​การ “สร้าง” Aggregate ที่​ซับซ้อน และ​รับประกัน​ว่าอ็อบเจ็กต์ใหม่​ถูกต้อง​ตาม invariant ตั้งแต่​แรก​เกิด — “สร้าง​ของ​ใหม่” (repository หา “ของ​เก่า”)Tactical Design ห่อ​หุ้ม​การ “สร้าง” Aggregate ที่​ซับซ้อน​ให้​ถูกต้อง​ตาม invariant ตั้งแต่​แรก — Evans เปรียบ​ว่า “factory สร้าง​ของ​ใหม่ ส่วน repository หา​ของ​เก่า”


Anemic Domain ModelAnemic Domain Modelanti-pattern ที่อ็อบเจ็กต์มี​แต่ getter/setter ไร้​พฤติกรรม ตรรกะ​ถูก​ดึง​ไป​ไว้​ใน service ทั้งหมด — จ่าย​ค่า domain model แต่​ไม่​ได้​ประโยชน์​ของ​มัน​เลยTactical Design คือ​กับดัก​ที่​พบ​บ่อย​ที่สุด: อ็อบเจ็กต์ดูเหมือน domain model มีชื่อ​ตรง​กับ​คำ​ใน​ธุรกิจ แต่ “แทบ​ไม่มี​พฤติกรรม” เป็น​เพียง “ถุง​ใส่ getter/setter” ส่วน​ตรรกะ​ทั้งหมด​ถูก​ผลัก​ไป​ไว้​ใน​ชั้น service Fowler เรียก​ว่า​ขัด​กับ​แก่น​ของ OOP โดย​สิ้นเชิง เพราะ OOP คือ​การ​รวม​ข้อมูล​กับ​พฤติกรรม​เข้า​ด้วย​กัน

ปัญหา​ของ anemic domain model คือ​มัน​แบก​ต้นทุน​ทั้งหมด​ของ domain model โดย​ไม่​ได้​ประโยชน์​ของ​มัน​เลย

— ใจความ​จาก Martin Fowler

แต่ — ไม่ใช่​ทุก​ที่​ต้อง​รวย

Microsoft ตั้ง​ข้อสังเกต​ว่า ถ้า Bounded Context นั้น​ง่าย​มาก (เป็น CRUD ล้วน เช่น catalog สินค้า) anemic model ก็ “ดีพอ” ได้ ไม่​ต้อง​ฝืน​ใช้ DDD เต็ม​รูปแบบ​ทุก​ที่ — ขึ้น​กับ​ว่า​กำลัง​สร้าง​อะไร (เรื่อง​นี้​เชื่อม​กับ Stage 5)


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

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

  • Entity — ขยาย​ความ​เรื่อง​การ​แยกแยะ​ด้วย identity และ​ทำไม Entity ต้อง “มี​พฤติกรรม” ไม่ใช่​แค่​เก็บ​ข้อมูล
  • Value Object — หลักการ immutability และ self-validation ที่​ตัวอย่าง Money ใช้
  • Aggregate — รายละเอียด​เรื่อง Aggregate Root และ​ขอบเขต​ความ​ถูกต้อง (invariant boundary) ที่​ต่อยอด​จาก​สี่​กฎ​ของ Vernon
  • Repository — pattern เต็ม​รูปแบบ​เบื้องหลัง interface OrderRepository และ​เหตุผล​ที่ domain ไม่​ควร​ขึ้น​กับ ORM
  • Anemic Domain Model — วิเคราะห์ anti-pattern นี้​ลึก​ขึ้น พร้อม​วิธี​สังเกต​และ​หลีก​เลี่ยง

เช็กความเข้าใจ — Stage 3

ข้อ 1 / 5

อะไรคือความต่างหลักระหว่าง Entity กับ Value Object?