Ogni volta che uno sviluppatore chiede "qual è il punto di fare questo?", Ciò che realmente significano è "Non vedo alcun caso d'uso in cui farlo offre un vantaggio". A tal fine, lascia che ti mostri alcuni esempi.
Tutti gli esempi saranno basati su questo semplice modello di dati:
Un Person
soggetto ha cinque proprietà:Id, FirstName, LastName, Age, CityId
E puoi supporre che l'applicazione utilizzi questi dati in molti modi diversi (report, moduli, popup, ...).
L'intera applicazione esiste già. Tutto ciò che menziono è una modifica alla base di codice esistente. Questo è importante da ricordare.
Esempio 1 - Modifica della struttura dati sottostante - Senza DTO
I requisiti sono cambiati. L'età della persona deve essere recuperata in modo dinamico dal database del governo (supponiamo in base al nome e cognome).
Poiché non è più necessario archiviare il Age
valore localmente, è quindi necessario rimuoverlo Person
dall'entità. È importante qui rendersi conto che l'entità rappresenta i dati del database e nient'altro. Se non è nel database, non è nell'entità.
Quando recuperi l'età dal servizio web del governo, questo verrà archiviato in un oggetto diverso (o int).
Ma il tuo frontend mostra ancora un'età. Tutte le viste sono state impostate per utilizzare la Person.Age
proprietà, che ora non esiste più. Si presenta un problema: tutte le viste che si riferiscono a Age
di una persona devono essere riparate .
Esempio 2 - Modifica della struttura dati sottostante - Con DTO
Nel vecchio sistema, c'è anche PersonDTO
entità con le stesse cinque proprietà: Id, FirstName, LastName, Age, CityId
. Dopo aver recuperato a Person
, il livello di servizio lo converte in a PersonDTO
e quindi lo restituisce.
Ma ora, i requisiti sono cambiati. L'età della persona deve essere recuperata in modo dinamico dal database del governo (supponiamo in base al nome e cognome).
Poiché non è più necessario archiviare il Age
valore localmente, è quindi necessario rimuoverlo Person
dall'entità. È importante qui rendersi conto che l'entità rappresenta i dati del database e nient'altro. Se non è nel database, non è nell'entità.
Tuttavia, poiché hai un intermediario PersonDTO
, è importante vedere che questa classe può mantenere la Age
proprietà. Il livello di servizio recupererà il Person
, lo convertirà in un PersonDTO
, quindi recupererà anche l'età della persona dal servizio web del governo, memorizzerà quel valore PersonDTO.Age
e passerà quell'oggetto.
La parte importante qui è che chiunque usi il livello di servizio non vede alcuna differenza tra il vecchio e il nuovo sistema . Questo include il tuo frontend. Nel vecchio sistema, ha ricevuto un PersonDTO
oggetto completo . E nel nuovo sistema, riceve ancora un PersonDTO
oggetto completo . Non è necessario aggiornare le visualizzazioni .
Questo è ciò che intendiamo quando usiamo la frase separazione delle preoccupazioni : ci sono due diverse preoccupazioni (memorizzazione dei dati nel database, presentazione dei dati al frontend) e hanno bisogno di un diverso tipo di dati ciascuno. Anche se questi due tipi di dati contengono gli stessi dati in questo momento, ciò potrebbe cambiare in futuro.
Nell'esempio dato, Age
c'è una differenza tra i due tipi di dati: Person
(l'entità database) non ha bisogno di Age
, ma PersonDTO
(il tipo di dati frontend) ne ha bisogno.
Separando le preoccupazioni (= creando tipi di dati separati) dall'inizio, la base di codice è molto più resistente alle modifiche apportate al modello di dati.
Potresti sostenere che avere un oggetto DTO, quando una nuova colonna viene aggiunta al database, significa che devi fare un doppio lavoro, aggiungendo la proprietà sia nell'entità che nel DTO. Questo è tecnicamente corretto. Richiede un po 'di sforzo in più per mantenere due classi invece di una.
Tuttavia, è necessario confrontare lo sforzo richiesto. Quando vengono aggiunte una o più nuove colonne, copiare / incollare alcune proprietà non richiede così tanto tempo. Quando il modello di dati cambia strutturalmente, dover cambiare il frontend, possibilmente in modi che causano solo bug in fase di esecuzione (e non in fase di compilazione), richiede uno sforzo considerevolmente maggiore e richiede agli sviluppatori di andare a caccia di bug.
Potrei darti più esempi ma il principio sarà sempre lo stesso.
Riassumere
- Responsabilità separate (preoccupazioni) devono lavorare separatamente l'una dall'altra. Non dovrebbero condividere risorse come le classi di dati (ad es.
Person
)
- Solo perché un'entità e il suo DTO hanno le stesse proprietà, non significa che è necessario unirle nella stessa entità. Non tagliare gli angoli.
- Come esempio più evidente, supponiamo che il nostro database contenga paesi, canzoni e persone. Tutte queste entità hanno a
Name
. Ma solo perché hanno tutti una Name
proprietà, non significa che dovremmo farli ereditare da una EntityWithName
classe base condivisa . Le diverse Name
proprietà non hanno relazioni significative.
- Se una delle proprietà dovesse mai cambiare (ad es. Una canzone
Name
viene rinominata Title
, o una persona ottiene un FirstName
e LastName
), dovrai spendere più sforzi per annullare l'eredità di cui non hai nemmeno bisogno .
- Sebbene non sia così palese, la tua tesi secondo cui non hai bisogno di un DTO quando hai un'entità è la stessa. Stai guardando il momento , ma non ti stai preparando per eventuali cambiamenti futuri. SE l'entità e il DTO sono esattamente gli stessi e SE si può garantire che non ci saranno mai modifiche al modello di dati; allora hai ragione che puoi omettere il DTO. Ma il fatto è che non puoi mai garantire che il modello di dati non cambierà mai.
- Le buone pratiche non pagano sempre immediatamente. Potrebbe iniziare a ripagare in futuro, quando è necessario rivisitare una vecchia applicazione.
- Il principale killer dei codebase esistenti sta lasciando cadere la qualità del codice, rendendo continuamente più difficile mantenere il codebase, fino a quando non si trasforma in un inutile casino di spaghetti code che non è possibile mantenere.
- Le buone pratiche, come l'implementazione di una separazione delle preoccupazioni dal raggiungere, mirano a evitare quella pendenza scivolosa di cattiva manutenzione, al fine di mantenere la base di codice mantenibile il più a lungo possibile.
Come regola empirica per considerare le preoccupazioni di separazione, pensaci in questo modo:
Supponiamo che ogni preoccupazione (l'interfaccia utente, il database, la logica) sia gestita da una persona diversa in una posizione diversa. Possono comunicare solo via e-mail.
In una base di codice ben separata, una modifica a una particolare preoccupazione dovrà essere gestita da una sola persona:
- La modifica dell'interfaccia utente comporta solo lo sviluppo dell'interfaccia utente.
- La modifica del metodo di archiviazione dei dati comporta solo lo sviluppo del database.
- La modifica della logica aziendale coinvolge solo gli sviluppatori aziendali.
Se tutti questi sviluppatori utilizzassero la stessa Person
entità e venisse apportata una piccola modifica all'entità, tutti dovrebbero essere coinvolti nel processo.
Ma usando classi di dati separate per ogni livello, quel problema non è così diffuso:
- Finché lo sviluppatore del database può restituire un
PersonDTO
oggetto valido , gli sviluppatori aziendali e dell'interfaccia utente non si preoccupano di aver modificato il modo in cui i dati vengono archiviati / recuperati.
- Finché gli sviluppatori aziendali archiviano i dati nel database e forniscono i dati necessari al frontend, gli sviluppatori del database e dell'interfaccia utente non si preoccupano se decide di rielaborare le sue regole aziendali.
- Finché l'interfaccia utente può essere progettata in base a `PersonViewModel, lo sviluppatore dell'interfaccia utente può creare l'interfaccia utente come desidera. Il database e gli sviluppatori aziendali non si preoccupano di come viene fatto, dal momento che non li influenza.
La frase chiave qui è perché non li influenza . L'attuazione di una buona separazione delle preoccupazioni mira a ridurre al minimo le conseguenze (e quindi il coinvolgimento di altre parti).
Naturalmente, alcune importanti modifiche non possono evitare di includere più di una persona, ad esempio quando un'entità completamente nuova viene aggiunta al database. Ma non sottovalutare la quantità di modifiche minori che è necessario apportare durante la vita di un'applicazione. Le principali modifiche sono una minoranza numerica.
What's the benefit of these conversions?
disaccoppiando il modello di dati di persistenza dal modello di dati (rappresentazione) offerto ai consumatori. I vantaggi del disaccoppiamento sono stati ampiamente discussi in SE Tuttavia, l'obiettivo alla base di DTO è quello di raccogliere in un'unica risposta tutte le informazioni ritenute necessarie ai client per salvare le chiamate al server. Ciò che rende più agevole la comunicazione client-server.