Blocchi fondamentali del DDD: entità e oggetti valore
Modellate il dominio con entità ricche e oggetti valore immutabili.
Blocchi fondamentali del DDD: entità e oggetti valore è una lezione PHP Academy gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento PHP Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso PHP Academy include 4 lezioni in totale.
Il nucleo tattico
I pattern tattici del Domain-Driven Design forniscono un vocabolario per modellare i concetti di business nel codice. I due elementi costitutivi fondamentali sono le Entities, cioè oggetti definiti dalla propria identità nel tempo, e i Value Objects, cioè oggetti definiti dai propri attributi e trattati come immutabili. Distinguere correttamente questi concetti dà forma a tutto il resto del livello di dominio.
Identità e valore
Un’entità ha un’identità stabile che persiste anche quando cambiano i suoi attributi: un Customer rimane lo stesso cliente anche dopo aver cambiato casa. Un oggetto valore non ha un’identità; due oggetti valore con gli stessi attributi sono intercambiabili, come due banconote da 5 $. Si chieda: "se cambiassero tutti i campi, sarebbe ancora la stessa cosa?" Se sì, si tratta di un’entità.
Un oggetto valore
Gli oggetti valore sono immutabili e auto-validanti. Le proprietà readonly di PHP 8.1 impongono l’immutabilità; il costruttore protegge gli invarianti, così non può mai esistere un’istanza non valida.
<?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
Uguaglianza per valore
Gli oggetti valore vengono confrontati secondo l’uguaglianza strutturale, non per riferimento. È opportuno fornire un metodo equals() esplicito invece di affidarsi a ==, perché il confronto non rigoroso di PHP segue regole talvolta sorprendenti. Un oggetto valore Money deve considerare sia l’importo sia la 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
Immutabilità e metodi 'with'
Poiché gli oggetti valore sono immutabili, "modificarne" uno significa restituire una nuova istanza. Metodi come add() o withAmount() non modificano mai l’oggetto: producono un nuovo valore. In questo modo si eliminano i bug dovuti agli alias e gli oggetti valore possono essere condivisi liberamente nel dominio senza rischi.
<?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;
Un’entità
Un’entità è definita dalla propria identità, in genere un id generato dal dominio, non da un auto-increment del database. L’uguaglianza confronta gli id. I suoi attributi possono cambiare attraverso metodi ricchi di comportamento che proteggono gli invarianti.
<?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
Generazione dell’identità
È preferibile generare l’identità nel dominio, ad esempio tramite un UUID, prima della persistenza, invece di attendere il database. In questo modo è possibile costruire in memoria un’entità completamente valida, farvi riferimento tra gli aggregati ed eseguire test senza un database. L’id fa parte del modello, non è un semplice artefatto di archiviazione.
Comportamento ricco, non dati anemici
Un anti-pattern comune è il modello di dominio anemico: le entità sono insiemi di getter e setter pubblici, mentre tutta la logica risiede in classi "di servizio". Il DDD spinge a portare il comportamento nell’entità. $order->cancel() incapsula le regole dell’annullamento, invece di permettere ai chiamanti di modificare direttamente un campo di stato.
<?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
Oggetti valore ovunque
Sostituisca l’ossessione per i tipi primitivi con gli oggetti valore. Invece di passare ovunque string $email, int $cents e string $currency, li si racchiuda in Email e Money. Vantaggi:
- La validazione avviene una sola volta, durante la costruzione.
- Le regole di dominio, come la corrispondenza tra valute, risiedono insieme ai dati.
- Le firme dei tipi documentano l’intento e impediscono di confondere i valori.
Nota sulla mappatura della persistenza
Gli oggetti valore vengono spesso mappati su colonne incorporate anziché su tabelle proprie (Doctrine #[Embeddable]). Le entità vengono mappate su righe identificate dalla loro identità. Mantenga i dettagli di persistenza fuori dagli oggetti del dominio: il modello non dovrebbe sapere di essere memorizzato in MySQL. Un ORM o un mapper scritto manualmente traduce tra il dominio ricco e il database.
Oggetti valore compositi
Gli oggetti valore possono essere composti. Un Address raggruppa diversi primitivi in un unico concetto coerente, con una propria uguaglianza e una propria formattazione. L’entità contiene così un singolo tipo ricco invece di cinque stringhe indipendenti, mentre le regole dell’indirizzo risiedono in un unico punto.
<?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;
Verifica rapida
Entità o oggetto valore?
Riepilogo
Ha appreso i due elementi fondamentali del DDD. Le entità sono definite da un’identità stabile, racchiudono un comportamento ricco e proteggono i propri invarianti attraverso i metodi. Gli oggetti valore sono immutabili, auto-validanti e confrontati in base all’uguaglianza degli attributi, sostituendo l’ossessione per i tipi primitivi. Modellarli correttamente, inserendo la logica negli oggetti invece di creare contenitori di dati anemici, è la base per gli aggregati, i repository e il resto del livello di dominio.
Impara PHP con un tutor IA — gratis
Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.
- Corsi
- 49
- Lezioni
- 195
Domande Frequenti
La lezione «Blocchi fondamentali del DDD: entità e oggetti valore» è gratuita?
Sì — il testo completo di «Blocchi fondamentali del DDD: entità e oggetti valore» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso PHP Academy, passa a CoddyKit PRO. Il corso PHP Academy include 4 lezioni in totale.
Cosa imparerò in «Blocchi fondamentali del DDD: entità e oggetti valore»?
Modellate il dominio con entità ricche e oggetti valore immutabili. Eserciti PHP Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare PHP Academy?
Non è richiesta alcuna esperienza precedente. PHP Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.
Quanto tempo richiede la lezione «Blocchi fondamentali del DDD: entità e oggetti valore»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione PHP Academy?
Sì. Ogni lezione PHP Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Blocchi fondamentali del DDD: entità e oggetti valore
- Aggregati, repository e factory
- Eventi e servizi di dominio
- Bounded context e context mapping