DDD-byggblock: entiteter och värdeobjekt
Modellera domänen med rika entiteter och oföränderliga värdeobjekt.
DDD-byggblock: entiteter och värdeobjekt är en gratis lektion i PHP Academy på CoddyKit. Detta är lektion 1 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för PHP Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i PHP Academy innehåller totalt 4 lektioner.
Den taktiska kärnan
Domain-Driven Designs taktiska mönster ger dig ett ordförråd för att modellera affärsbegrepp i kod. De två grundläggande byggstenarna är Entities (objekt som definieras av sin identitet över tid) och Value Objects (objekt som definieras av sina attribut och behandlas som oföränderliga). Om du får denna åtskillnad rätt påverkar det allt annat i ditt domänlager.
Identitet kontra värde
En entitet har en stabil identitet som består även när dess attribut ändras: en Customer är samma kund även efter att kunden har flyttat. Ett värdeobjekt har ingen identitet; två värdeobjekt med samma attributvärden är utbytbara, ungefär som två sedlar på 5 dollar. Fråga: ”om varje fält ändras, är det fortfarande samma sak?” Om svaret är ja är det en entitet.
Ett värdeobjekt
Värdeobjekt är oföränderliga och självvaliderande. Egenskaper med readonly i PHP 8.1 säkerställer oföränderlighet; konstruktorn skyddar invarianterna så att en ogiltig instans aldrig kan existera.
<?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
Likhet baserad på värde
Värdeobjekt jämförs utifrån strukturell likhet, inte referens. Tillhandahåll en uttrycklig equals()-metod i stället för att förlita er på ==, eftersom PHP:s lösa jämförelseregler kan ge överraskande resultat. Ett Money-värdeobjekt måste ta hänsyn till både belopp och valuta.
<?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
Oföränderlighet och ”with”-metoder
Eftersom värdeobjekt är oföränderliga innebär det att ”ändra” ett sådant objekt att returnera en ny instans. Metoder som add() eller withAmount() ändrar aldrig objektet; de skapar ett nytt värde. Det eliminerar aliaseringsfel och gör värdeobjekt säkra att dela fritt i hela domänen.
<?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;
En entitet
En entitet definieras av sin identitet, vanligtvis ett domängenererat id, inte ett automatiskt inkrementerat id från databasen. Likhet jämför id:n. Attributen kan ändras genom metoder med rikt beteende som skyddar invarianterna.
<?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
Generering av identitet
Föredra att generera identiteten i domänen, till exempel ett UUID, före persistensen i stället för att vänta på databasen. Då kan en helt giltig entitet skapas i minnet, refereras till över aggregat och testas utan databas. Id:t är en del av modellen, inte en lagringsartefakt.
Rikt beteende, inte anemiska data
Ett vanligt antipattern är den anemiska domänmodellen: entiteterna består av samlingar av offentliga getters och setters, medan all logik ligger i ”service”-klasser. DDD flyttar beteendet in i entiteten. $order->cancel() kapslar in reglerna för att avbryta en order, i stället för att låta anroparen ändra ett statusfält direkt.
<?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
Värdeobjekt överallt
Ersätt överanvändning av primitiva typer med värdeobjekt. I stället för att skicka runt string $email, int $cents och string $currency paketeras de i Email och Money. Fördelar:
- Validering sker en gång, vid konstruktionen.
- Domänregler, till exempel matchning av valutor, hör ihop med datan.
- Typsignaturer dokumenterar avsikten och förhindrar sammanblandningar.
Anmärkning om persistensmappning
Värdeobjekt mappas ofta till inbäddade kolumner i stället för egna tabeller, exempelvis med Doctrine #[Embeddable]. Entiteter mappas till rader som identifieras av sin identitet. Håll persistensdetaljerna utanför själva domänobjekten: modellen ska inte känna till att den lagras i MySQL. Ett ORM-verktyg eller en egen mappare översätter mellan den rika domänen och databasen.
Sammansatta värdeobjekt
Värdeobjekt kan sättas samman. Ett Address samlar flera primitiva värden till ett sammanhållet koncept med egen likhet och formatering. Entiteten innehåller då en enda rik typ i stället för fem lösa strängar, och adressreglerna finns samlade på ett ställe.
<?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;
Snabbkontroll
Entitet eller värdeobjekt?
Sammanfattning
De två centrala byggblocken i DDD har nu presenterats. Entiteter definieras av en stabil identitet, har rikt beteende och skyddar sina invarianter genom metoder. Värdeobjekt är oföränderliga, självvaliderande och jämförs utifrån attributens likhet, vilket ersätter överanvändningen av primitiva typer. Att modellera dessa korrekt, med logik i objekten i stället för i anemiska datapåsar, är grunden för aggregat, repositories och resten av domänlagret.
Lär dig PHP med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 49
- Lektioner
- 195
Vanliga frågor
Är lektionen ”DDD-byggblock: entiteter och värdeobjekt” gratis?
Ja – hela texten till ”DDD-byggblock: entiteter och värdeobjekt” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i PHP Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i PHP Academy innehåller totalt 4 lektioner.
Vad lär jag mig i ”DDD-byggblock: entiteter och värdeobjekt”?
Modellera domänen med rika entiteter och oföränderliga värdeobjekt. Ni övar på PHP Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig PHP Academy?
Du behöver inga förkunskaper. Utbildningen i PHP Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.
Hur lång tid tar lektionen ”DDD-byggblock: entiteter och värdeobjekt”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här PHP Academy-lektionen?
Ja. Varje PHP Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- DDD-byggblock: entiteter och värdeobjekt
- Aggregat, repositories och factories
- Domänhändelser och domäntjänster
- Avgränsade kontexter och kontextmappning