องค์ประกอบพื้นฐาน DDD: เอนทิตีและออบเจ็กต์ค่า
จำลองโดเมนด้วยเอนทิตีที่มีพฤติกรรมครบถ้วนและออบเจ็กต์ค่าที่เปลี่ยนแปลงไม่ได้
องค์ประกอบพื้นฐาน DDD: เอนทิตีและออบเจ็กต์ค่า เป็นบทเรียน PHP Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน PHP Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน
แกนเชิงยุทธวิธี
รูปแบบเชิงยุทธวิธีของการออกแบบที่ขับเคลื่อนด้วยโดเมนช่วยให้คุณมีคำศัพท์สำหรับสร้างแบบจำลองแนวคิดทางธุรกิจในโค้ด ส่วนประกอบพื้นฐานสำคัญสองอย่างคือเอนทิตี (อ็อบเจกต์ที่กำหนดด้วยอัตลักษณ์ตลอดช่วงเวลา) และอ็อบเจกต์ค่า (อ็อบเจกต์ที่กำหนดด้วยแอตทริบิวต์และถือว่าเปลี่ยนแปลงไม่ได้) การแยกแยะสองสิ่งนี้ให้ถูกต้องจะกำหนดรูปแบบของทุกสิ่งที่เหลือในชั้นโดเมน
อัตลักษณ์เทียบกับค่า
เอนทิตี มีอัตลักษณ์ที่คงที่และยังคงเดิมแม้แอตทริบิวต์จะเปลี่ยนไป: Customer ยังคงเป็นลูกค้ารายเดิมแม้จะย้ายบ้าน ถ้าเป็น อ็อบเจ็กต์ค่า จะไม่มีอัตลักษณ์ ดังนั้นอ็อบเจ็กต์ค่าสองรายการที่มีแอตทริบิวต์เท่ากันจึงใช้แทนกันได้ เช่น ธนบัตรมูลค่า 5 ดอลลาร์สองใบ ลองถามว่า "หากช่องข้อมูลทุกช่องเปลี่ยนไป สิ่งนี้ยังเป็นสิ่งเดิมอยู่หรือไม่?" หากคำตอบคือใช่ สิ่งนั้นก็คือเอนทิตี
อ็อบเจ็กต์ค่า
อ็อบเจ็กต์ค่าเป็น สิ่งที่ไม่เปลี่ยนรูป และ ตรวจสอบความถูกต้องได้ด้วยตนเอง คุณสมบัติ readonly ของ PHP 8.1 จะบังคับให้ข้อมูลไม่เปลี่ยนรูป ส่วนตัวสร้างจะป้องกันเงื่อนไขคงตัว ทำให้ไม่มีทางสร้างอินสแตนซ์ที่ไม่ถูกต้องขึ้นมาได้
<?php
final class Email {
public function __construct(public readonly string $value) {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email: $value");
}
}
public function equals(Email $other): bool {
return strtolower($this->value) === strtolower($other->value);
}
}
$a = new Email('Jane@Example.com');
var_dump($a->equals(new Email('jane@example.com'))); // true
ความเท่าเทียมตามค่า
อ็อบเจ็กต์ค่าเปรียบเทียบกันด้วย ความเท่าเทียมเชิงโครงสร้าง ไม่ใช่การอ้างอิง ควรจัดเตรียมเมธอด equals() อย่างชัดเจน แทนการพึ่งพา == เนื่องจากการเปรียบเทียบแบบหลวมของ PHP มีกฎที่คาดเดาได้ยาก อ็อบเจ็กต์ค่า Money ต้องพิจารณาทั้งจำนวนเงินและสกุลเงิน
<?php
final class Money {
public function __construct(
public readonly int $cents,
public readonly string $currency
) {}
public function equals(Money $o): bool {
return $this->cents === $o->cents && $this->currency === $o->currency;
}
public function add(Money $o): self {
if ($this->currency !== $o->currency) {
throw new DomainException('Currency mismatch');
}
return new self($this->cents + $o->cents, $this->currency);
}
}
$sum = (new Money(500, 'USD'))->add(new Money(250, 'USD'));
echo $sum->cents, PHP_EOL; // 750
ความไม่เปลี่ยนรูปและเมธอดสำหรับสร้างค่าที่ปรับแล้ว
เนื่องจากอ็อบเจ็กต์ค่าไม่เปลี่ยนรูป การ "เปลี่ยนแปลง" อ็อบเจ็กต์หนึ่งจึงหมายถึงการคืนค่าเป็น อินสแตนซ์ใหม่ เมธอดอย่าง add() หรือ withAmount() จะไม่แก้ไขข้อมูลเดิม แต่จะสร้างค่าใหม่แทน วิธีนี้ช่วยขจัดข้อผิดพลาดจากการอ้างอิงร่วม และทำให้อ็อบเจ็กต์ค่าปลอดภัยต่อการใช้ร่วมกันอย่างอิสระทั่วทั้งโดเมน
<?php
final class DateRange {
public function __construct(
public readonly DateTimeImmutable $start,
public readonly DateTimeImmutable $end
) {
if ($end < $start) { throw new InvalidArgumentException('end before start'); }
}
public function withEnd(DateTimeImmutable $end): self {
return new self($this->start, $end);
}
}
$r = new DateRange(new DateTimeImmutable('2026-01-01'), new DateTimeImmutable('2026-01-10'));
$r2 = $r->withEnd(new DateTimeImmutable('2026-02-01'));
echo $r->end->format('Y-m-d'), ' / ', $r2->end->format('Y-m-d'), PHP_EOL;
เอนทิตี
เอนทิตี ถูกกำหนดด้วยอัตลักษณ์ ซึ่งโดยทั่วไปคือรหัสที่สร้างโดยโดเมน ไม่ใช่รหัสที่เพิ่มขึ้นอัตโนมัติจากฐานข้อมูล การเปรียบเทียบความเท่าเทียมจะเปรียบเทียบรหัส แอตทริบิวต์ของเอนทิตีสามารถเปลี่ยนแปลงผ่านเมธอดที่มีพฤติกรรมและปกป้องเงื่อนไขคงตัวได้
<?php
final class Customer {
private Email $email;
public function __construct(
public readonly string $id,
Email $email
) { $this->email = $email; }
public function changeEmail(Email $new): void { $this->email = $new; }
public function email(): Email { return $this->email; }
public function sameIdentityAs(Customer $o): bool { return $this->id === $o->id; }
}
$c = new Customer('cus_1', new Email('a@b.com'));
$c->changeEmail(new Email('c@d.com'));
echo $c->email()->value, PHP_EOL; // c@d.com
การสร้างอัตลักษณ์
ควรสร้างอัตลักษณ์ภายในโดเมน เช่น UUID ก่อนการบันทึกลงที่จัดเก็บ แทนที่จะรอฐานข้อมูล วิธีนี้ช่วยให้คุณสร้างเอนทิตีที่ถูกต้องสมบูรณ์ในหน่วยความจำ อ้างอิงเอนทิตีนั้นข้ามแอกกริเกต และทดสอบได้โดยไม่ต้องใช้ฐานข้อมูล รหัสประจำตัวเป็นส่วนหนึ่งของแบบจำลอง ไม่ใช่สิ่งที่เกิดขึ้นจากการจัดเก็บข้อมูล
พฤติกรรมที่สมบูรณ์ ไม่ใช่ข้อมูลไร้พฤติกรรม
รูปแบบที่มักพบและควรหลีกเลี่ยงคือ แบบจำลองโดเมนที่ไร้พฤติกรรม: เอนทิตีเป็นเพียงกลุ่มตัวอ่านและตัวกำหนดค่าที่เปิดให้เข้าถึงได้ ขณะที่ตรรกะทั้งหมดอยู่ในคลาส "บริการ" DDD ผลักดันให้ใส่พฤติกรรม เข้าไปใน เอนทิตี $order->cancel() จึงห่อหุ้มกฎการยกเลิกไว้ แทนที่จะปล่อยให้ผู้เรียกเปลี่ยนฟิลด์สถานะโดยตรง
<?php
final class Order {
private string $status = 'open';
public function __construct(public readonly string $id) {}
public function cancel(): void {
if ($this->status === 'shipped') {
throw new DomainException('Cannot cancel a shipped order');
}
$this->status = 'cancelled';
}
public function status(): string { return $this->status; }
}
$o = new Order('o1');
$o->cancel();
echo $o->status(), PHP_EOL; // cancelled
อ็อบเจ็กต์ค่าทั่วทั้งระบบ
แทนที่การยึดติดกับชนิดข้อมูลพื้นฐานด้วยอ็อบเจ็กต์ค่า แทนที่จะส่ง string $email int $cents และ string $currency ไปทั่วระบบ ให้ห่อหุ้มข้อมูลเหล่านี้ไว้ใน Email และ Money ประโยชน์ที่ได้รับ:
- ตรวจสอบความถูกต้องเพียงครั้งเดียวเมื่อสร้าง
- กฎของโดเมน เช่น การจับคู่สกุลเงิน อยู่ร่วมกับข้อมูล
- ลายเซ็นชนิดข้อมูลสื่อเจตนาได้ชัดเจนและป้องกันการใช้ข้อมูลสับสนกัน
หมายเหตุการแมปเพื่อการจัดเก็บ
อ็อบเจ็กต์ค่ามักแมปกับ คอลัมน์แบบฝัง แทนการมีตารางของตนเอง เช่น #[Embeddable] ของด็อกทริน ส่วนเอนทิตีจะแมปกับแถวข้อมูลที่มีอัตลักษณ์เป็นกุญแจ อย่านำข้อกังวลด้านการจัดเก็บมาไว้ในอ็อบเจ็กต์โดเมนโดยตรง แบบจำลองไม่ควรรู้ว่าตนเองถูกจัดเก็บอยู่ใน MySQL เครื่องมือแมปออบเจ็กต์-เชิงสัมพันธ์หรือตัวแมปที่เขียนขึ้นเองจะทำหน้าที่แปลงระหว่างโดเมนที่มีพฤติกรรมสมบูรณ์กับฐานข้อมูล
อ็อบเจ็กต์ค่าแบบประกอบ
อ็อบเจ็กต์ค่าประกอบกันได้ Address จะรวมชนิดข้อมูลพื้นฐานหลายรายการเป็นแนวคิดเดียวที่สอดคล้องกัน พร้อมการเปรียบเทียบความเท่าเทียมและการจัดรูปแบบของตนเอง จากนั้นเอนทิตีจะเก็บชนิดข้อมูลที่มีความหมายครบถ้วนเพียงชนิดเดียว แทนการเก็บสตริงที่ไม่เชื่อมโยงกันห้ารายการ และกฎของที่อยู่จะอยู่รวมกันในที่เดียว
<?php
final class Address {
public function __construct(
public readonly string $street,
public readonly string $city,
public readonly string $postcode
) {
if ($postcode === '') { throw new InvalidArgumentException('postcode required'); }
}
public function equals(Address $o): bool {
return $this->street === $o->street
&& $this->city === $o->city
&& $this->postcode === $o->postcode;
}
public function oneLine(): string {
return "{$this->street}, {$this->city} {$this->postcode}";
}
}
echo (new Address('1 Main St', 'Ankara', '06000'))->oneLine(), PHP_EOL;
ตรวจสอบอย่างรวดเร็ว
เอนทิตีหรืออ็อบเจ็กต์ค่า?
สรุปทบทวน
คุณได้เรียนรู้หน่วยพื้นฐานหลักสองอย่างของ DDD แล้ว เอนทิตี ถูกกำหนดด้วยอัตลักษณ์ที่คงที่ มีพฤติกรรมที่สมบูรณ์ และปกป้องเงื่อนไขคงตัวผ่านเมธอด อ็อบเจ็กต์ค่า ไม่เปลี่ยนรูป ตรวจสอบความถูกต้องได้ด้วยตนเอง และเปรียบเทียบกันด้วยความเท่าเทียมของแอตทริบิวต์ จึงช่วยแทนที่การยึดติดกับชนิดข้อมูลพื้นฐาน การสร้างแบบจำลองอย่างถูกต้อง โดยใส่ตรรกะไว้ในอ็อบเจ็กต์แทนการใช้กลุ่มข้อมูลไร้พฤติกรรม เป็นรากฐานของแอกกริเกต รีโพซิทอรี และส่วนที่เหลือของชั้นโดเมน
เรียนรู้ PHP ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 49
- บทเรียน
- 195
คำถามที่พบบ่อย
บทเรียน “องค์ประกอบพื้นฐาน DDD: เอนทิตีและออบเจ็กต์ค่า” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “องค์ประกอบพื้นฐาน DDD: เอนทิตีและออบเจ็กต์ค่า” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส PHP Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “องค์ประกอบพื้นฐาน DDD: เอนทิตีและออบเจ็กต์ค่า”
จำลองโดเมนด้วยเอนทิตีที่มีพฤติกรรมครบถ้วนและออบเจ็กต์ค่าที่เปลี่ยนแปลงไม่ได้ คุณปฏิบัติ PHP Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน PHP Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน PHP Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “องค์ประกอบพื้นฐาน DDD: เอนทิตีและออบเจ็กต์ค่า” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน PHP Academy นี้ได้ไหม
ได้ บทเรียน PHP Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- องค์ประกอบพื้นฐาน DDD: เอนทิตีและออบเจ็กต์ค่า
- แอกกริเกต รีโพซิทอรี และแฟกทอรี
- เหตุการณ์โดเมนและบริการโดเมน
- บริบทที่มีขอบเขตและการทำแผนผังบริบท