PHP Academy · Oppitunti

DDD:n rakennuspalikat: entiteetit ja arvo-oliot

Mallintakaa toimialue monipuolisten entiteettien ja muuttumattomien arvo-olioiden avulla.

Oppitunti 1/413 vaihetta

DDD:n rakennuspalikat: entiteetit ja arvo-oliot on ilmainen PHP Academy-oppitunti CoddyKitissä. Tämä on oppitunti 1/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu PHP Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. PHP Academy-kurssilla on yhteensä 4 oppituntia.

Taktinen ydin

Toimialuevetoisen suunnittelun taktiset mallit tarjoavat sanaston liiketoimintakäsitteiden mallintamiseen koodissa. Kaksi perustavanlaatuista rakennuspalikkaa ovat entiteetit (oliot, jotka määritellään identiteettinsä perusteella ajan kuluessa) ja arvo-oliot (oliot, jotka määritellään attribuuttiensa perusteella ja joita käsitellään muuttumattomina). Tämän eron ymmärtäminen oikein muovaa kaikkea muuta toimialuekerroksessanne.

Identiteetti vai arvo

Entiteetillä on vakaa identiteetti, joka säilyy, vaikka sen attribuutit muuttuvat: Customer on sama asiakas senkin jälkeen, kun hän muuttaa. Arvo-oliolla ei ole identiteettiä; kaksi arvo-oliota, joilla on samat attribuutit, ovat keskenään vaihdettavissa, kuten kaksi 5 dollarin seteliä. Kysykää: "jos jokainen kenttä muuttuu, onko kyse edelleen samasta asiasta?" Jos on, kyseessä on entiteetti.

Arvo-olio

Arvo-oliot ovat muuttumattomia ja itsensä validoivia. PHP 8.1:n readonly-ominaisuudet varmistavat muuttumattomuuden; konstruktori varmistaa invariantit, joten virheellistä instanssia ei voi koskaan olla olemassa.

<?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

Yhtäsuuruus arvon perusteella

Arvo-olioita verrataan rakenteellisen yhtäsuuruuden perusteella, ei viitteen perusteella. Tarjotkaa eksplisiittinen equals()-metodi sen sijaan, että luottaisitte ==-operaattoriin, koska PHP:n löyhällä vertailulla on yllättäviä sääntöjä. Money-arvo-olion on otettava huomioon sekä summa että valuutta.

<?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

Muuttumattomuus ja 'with'-metodit

Koska arvo-oliot ovat muuttumattomia, yhden "muuttaminen" tarkoittaa uuden instanssin palauttamista. add()- tai withAmount()-metodit eivät koskaan muuta oliota, vaan tuottavat uuden arvon. Tämä poistaa aliasointivirheet ja tekee arvo-olioista turvallisesti jaettavia kaikkialla toimialueella.

<?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;

Entiteetti

Entiteetin määrittää identiteetti, tyypillisesti toimialueella luotu tunniste, ei tietokannan automaattisesti kasvava tunniste. Yhtäsuuruus perustuu tunnisteisiin. Sen attribuutit voivat muuttua käyttäytymistä sisältävien metodien kautta, ja nämä metodit suojaavat invariantteja.

<?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

Identiteetin muodostaminen

Identiteetti kannattaa luoda toimialueella (esimerkiksi UUID) ennen pysyvään tallennukseen siirtymistä tietokannan odottamisen sijaan. Näin täysin kelvollisen entiteetin voi muodostaa muistissa, siihen voi viitata eri aggregaateista ja testejä voi tehdä ilman tietokantaa. Tunniste on mallin osa, ei tallennuksen sivutuote.

Monipuolinen toiminta, ei aneemista dataa

Yleinen anti-pattern on aneeminen toimialuemalli: entiteetit ovat julkisten getter- ja setter-metodien muodostamia tietosäkkejä, kun taas kaikki logiikka sijaitsee "service"-luokissa. DDD siirtää toiminnallisuuden entiteetin sisään. $order->cancel() kapseloi peruutuksen säännöt sen sijaan, että kutsuja muuttaisi tilakenttää suoraan.

<?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

Arvo-olioita kaikkialle

Korvatkaa primitiivien pakkomielteinen käyttö arvo-olioilla. Sen sijaan että välittäisitte string $email-, int $cents- ja string $currency-arvoja ympäriinsä, kääritkää ne Email- ja Money-olioihin. Hyödyt:

  • Validointi tapahtuu kerran, luonnin yhteydessä.
  • Toimialuesäännöt (valuuttojen täsmääminen) ovat datan yhteydessä.
  • Tyyppimääritykset dokumentoivat tarkoituksen ja estävät arvojen sekoittamisen.

Huomautus persistenssikuvauksesta

Arvo-oliot kuvataan usein upotetuiksi sarakkeiksi omien taulujensa sijaan (Doctrine #[Embeddable]). Entiteetit kuvataan riveiksi, joiden avaimena on niiden identiteetti. Pitäkää persistenssiin liittyvät asiat erillään toimialueolioista: mallin ei pitäisi tietää, että se tallennetaan MySQL-tietokantaan. ORM tai käsin kirjoitettu mapper muuntaa rikkaan toimialueen ja tietokannan välillä.

Koostetut arvo-oliot

Arvo-olioita voi koostaa. Address kokoaa useita primitiivejä yhdeksi yhtenäiseksi käsitteeksi, jolla on oma yhtäsuuruus- ja muotoilulogiikkansa. Entiteetti sisältää tällöin yhden rikkaan tyypin viiden irrallisen merkkijonon sijaan, ja osoitteen säännöt ovat yhdessä paikassa.

<?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;

Pikatarkistus

Entiteetti vai arvo-olio?

Kertaus

Opitte DDD:n kaksi keskeistä rakennuspalikkaa. Entiteetit määritellään vakaan identiteetin perusteella, ne sisältävät monipuolista toimintaa ja suojaavat invarianttejaan metodeilla. Arvo-oliot ovat muuttumattomia ja itsensä validoivia, ja niitä verrataan attribuuttien yhtäsuuruuden perusteella, mikä korvaa primitiivien pakkomielteisen käytön. Oikein mallinnetut oliot, joissa logiikka on olioiden sisällä aneemisten tietosäkkien sijaan, muodostavat perustan aggregaateille, repositorioille ja muulle toimialuekerrokselle.

Aloita maksutta

Opi PHP tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
49
Oppitunnit
195

Usein kysytyt kysymykset

Onko oppitunti ”DDD:n rakennuspalikat: entiteetit ja arvo-oliot” ilmainen?

Kyllä – oppitunnin ”DDD:n rakennuspalikat: entiteetit ja arvo-oliot” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko PHP Academy-kurssin, päivitä CoddyKit PROhon. PHP Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”DDD:n rakennuspalikat: entiteetit ja arvo-oliot”?

Mallintakaa toimialue monipuolisten entiteettien ja muuttumattomien arvo-olioiden avulla. Harjoittelet PHP Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni PHP Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin PHP Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 1/4.

Kuinka kauan ”DDD:n rakennuspalikat: entiteetit ja arvo-oliot”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä PHP Academy-oppitunnilla?

Kyllä. Jokainen PHP Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. DDD:n rakennuspalikat: entiteetit ja arvo-oliot
  2. Aggregaatit, repositoriot ja tehtaat
  3. Toimialuetapahtumat ja toimialuepalvelut
  4. Rajatut kontekstit ja kontekstikartoitus
← Takaisin: PHP Academy