DTO vs VO in PHP

Maico Orazio
weBeetle
Published in
4 min readJan 10, 2024

--

I Data Transfer Objects (DTO) e i Value Objects (VO) sono particolarmente utili quando si lavora con linguaggi tipizzati dinamicamente come PHP, dove il passaggio all’utilizzo di tipi più strutturati è fondamentale per migliorare la qualità delle nostre applicazioni, nonché la leggibilità del codice.

Cos’è un DTO (Data Transfer Object)?

Un DTO è un oggetto che contiene dati e definisce la struttura di questi dati. Lo schema è definito dai nomi dei campi e dai loro tipi.

Sostanzialmente può solo garantire che tutti o alcuni dati siano presenti, semplicemente affidandosi alla rigidità del linguaggio di programmazione: se un costruttore ha un parametro obbligatorio di tipo string, è necessario passare una stringa per istanziare l'oggetto. Tuttavia, un DTO non fornisce alcuna garanzia sulla validità di questi dati: le stringhe potrebbero essere vuote, gli interi potrebbero essere negativi, ecc.

final class ADtoExample
{
public function __construct(
public readonly string $field,
// ...
) {
}
}

Cos’è un VO (Value Object)?

Un VO è un oggetto che fornisce una garanzia di validità sui dati che contiene. Sono rappresentazioni immutabili di dati, consapevoli delle regole di business specifiche del dominio. Ci aiutano a scrivere codice più robusto e leggibile, riducendo al minimo i bug.

Un value object è un oggetto che rappresenta un concetto nel dominio di un’applicazione. È immutabile, non ha identità e incapsula un valore.

final class AnVoExample
{
private function __construct(
private readonly string $value,
// ...
) {
}

public static function fromValue(
string $value
): self
{
match (true) {
empty($value) => throw new \InvalidArgumentException('value is empty'),
default => true
};
return new self($value);
}
}

Un VO offre diversi vantaggi, tra cui:

  • Specificità del dominio: possono essere utilizzati per rappresentare valori specifici del dominio. Ad esempio, la classe EmailAddress può essere utilizzata per rappresentare solo indirizzi di posta elettronica;
  • Riduzione degli errori: aiutano a ridurre gli errori perché incapsulano i valori e applicano regole di business;
  • Migliore chiarezza: rendono il codice più chiaro e comprensibile;
  • Migliore testabilità: sono più facili da testare rispetto ai tipi di dati primitivi;

Mentre un DTO si occupa della struttura dei dati, un VO offre anche la prova che i dati corrispondono alle aspettative. Quando la classe del VO viene utilizzata come parametro, proprietà o tipo restituito, sappiamo che abbiamo a che fare con un valore valido.

Come usare questi tipi di oggetti?

DTO

Un DTO dovrebbe essere utilizzato solo in due posti: dove i dati entrano nell’applicazione o dove escono dall’applicazione. Aiutano a incapsulare i dati, facilitandone la manipolazione e il passaggio tra le diverse parti della base di codice.

Alcuni esempi:

  • Quando un controller riceve una richiesta HTTP POST, i dati della richiesta possono avere qualsiasi forma: possiamo utilizzare un DTO per avere i dati con uno schema noto (chiavi e tipi verificati).
  • Quanto effettuiamo una richiesta HTTP POST a un servizio web: possiamo prima raccogliere i dati di input in un DTO e quindi serializzarli in un corpo della richiesta che il nostro client HTTP può inviare al servizio.
  • Per le query: possiamo usare un DTO per rappresentare il risultato della query.
  • Quando riceviamo una richiesta HTTP GET: possiamo prima deserializzare la risposta API in un DTO, in modo da poter applicare ad esso uno schema noto invece di accedere direttamente alle chiavi dell’array e indovinare i tipi.

VO

Un VO viene utilizzato ogni volta che vogliamo verificare che un valore corrisponda alle nostre aspettative e non vogliamo verificarlo nuovamente.

Ad esempio, sono particolarmente utili per rappresentare i dati di input e output di un API. In questo modo, possiamo essere certi che i dati ricevuti dall’API siano validi e che i dati inviati all’API siano corretti.

Possiamo utilizzarlo anche per creare comportamenti legati a un valore particolare. Ad esempio, se abbiamo un VO EmailAddress, sappiamo che il valore è stato già verificato per essere un indirizzo email valido, quindi non dobbiamo controllarlo di nuovo in altri posti. Possiamo anche aggiungere metodi all'oggetto che estraggono ad esempio il nome utente, o il nome host, dall'indirizzo email.

Facciamo un esempio di un’applicazione in cui dobbiamo gestire i prezzi: Senza l’utilizzo dei VO, il nostro codice potrebbe essere pieno di funzioni che ricevono valori (come numeri amount e stringhe currency) e ognuna di essa dovrebbe ripetere la convalida sui valori per evitare che si verificano errori. Lesinare su questi controlli rende il nostro codice più pulito, ma rischia di introdurre bug. È qui che primeggiano i VO.

enum Currency : string
{
case Dollar = 'USD';
case Euro = 'EUR';
}

readonly class Price implements Stringable
{
public function __construct(
private float $amount,
private Currency $currency
)
{
match (true) {
($this->amount <= 0) => throw new InvalidPriceAmountException('amount is negative'),
default => true
};
}

public function __toString(): string
{
return number_format($this->amount, 2) . ' ' . $this->currency->value;
}

public function getAmount(): float
{
return $this->amount;
}

public function getCurrency(): string
{
return $this->currency->value;
}
}

$price = new Price(15, ‘$’); // Invalid Argument Exception
$price = new Price(0, Currency::Dollar); // InvalidPriceAmountException

$price = new Price(29.99, Currency::Dollar); // OK
echo $price; // 29.99 USD

Ogni volta che vogliamo rappresentare un prezzo nel nostro codice, possiamo creare un VO Price e fidarci della convalida interna effettuata durante l’istanziazione. Ciò significa che per il ciclo di vita di quell'oggetto nel nostro codice, non abbiamo bisogno di riconvalidarlo ovunque venga passato tra le funzioni.

Per concludere, DTO e VO sono due pattern di progettazione utili per migliorare la qualità e la leggibilità del codice in PHP.

DTO sono oggetti che contengono dati e definisce la struttura di questi dati. Sono utili per trasferire dati tra diverse parti di un’applicazione.

VO sono oggetti che forniscono una garanzia di validità sui dati che contengono. Sono utili per rappresentare valori specifici del dominio e per applicare regole di business.

Buon lavoro 👨‍💻

--

--

Maico Orazio
weBeetle

Senior Web Application Developer. I'm a software engineer, a passionate coder, and a web developer. I am a fan of technology. #php #symfony #javascript #reactjs