A che cosa serve DTO anziché Entity?


18

Sto lavorando su un'applicazione RCP, sono nuovo di questa applicazione.

I bean di primavera vengono utilizzati per scrivere la logica aziendale per salvare / recuperare entità.

Invece di inviare entità direttamente al client, ci stiamo convertendo in DTO e popolando il client. Durante il salvataggio, stiamo nuovamente convertendo DTO in entità e risparmiando.

Qual è il vantaggio di queste conversioni? Qualcuno può spiegare?


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.
Laiv,


Il tuo esempio è carino. Quando sei il cliente (visualizzazioni ...) è doloroso cambiare, ma il problema più grande è quando il sistema ha già integrazioni di terze parti, è impossibile cambiare (contratto, commissioni ...). Se il tuo sistema avrà l'integrazione di terze parti, usa sempre DTO.
Lucas Gonçalves,

Risposte:


44

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 Personsoggetto 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 Agevalore localmente, è quindi necessario rimuoverlo Persondall'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.Ageproprietà, che ora non esiste più. Si presenta un problema: tutte le viste che si riferiscono a Agedi una persona devono essere riparate .


Esempio 2 - Modifica della struttura dati sottostante - Con DTO

Nel vecchio sistema, c'è anche PersonDTOentità con le stesse cinque proprietà: Id, FirstName, LastName, Age, CityId. Dopo aver recuperato a Person, il livello di servizio lo converte in a PersonDTOe 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 Agevalore localmente, è quindi necessario rimuoverlo Persondall'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 Ageproprietà. 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.Agee 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 PersonDTOoggetto completo . E nel nuovo sistema, riceve ancora un PersonDTOoggetto 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, Agec'è 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 Nameproprietà, non significa che dovremmo farli ereditare da una EntityWithNameclasse base condivisa . Le diverse Nameproprietà non hanno relazioni significative.
    • Se una delle proprietà dovesse mai cambiare (ad es. Una canzone Nameviene rinominata Title, o una persona ottiene un FirstNamee 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 Personentità 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 PersonDTOoggetto 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.


Risposta completa, grazie. Ho delle domande; Apprezzo se rispondi: 1 - Correggimi, se sbaglio. L'oggetto business o l' oggetto vista è per il trasferimento di dati tra il livello presentazione e il livello aziendale e l' oggetto entità è per il trasferimento di dati tra livello aziendale e livello di accesso ai dati . e DTO può essere usato come BO stupido. 2 - Supponi che due punti di vista richiedano informazioni diverse su un'azienda, quindi abbiamo bisogno di due DTO aziendali diversi?
Arash,

1
@Arash (1) "DTO" è in realtà una definizione generale per qualsiasi classe di dati utilizzata per lo scambio tra due livelli. Un oggetto business e un oggetto vista sono entrambi DTO. (2) Dipende molto da molte cose. La creazione di un nuovo dto per ogni raccolta di campi richiesta è un'operazione complessa. Non c'è nulla di intrinsecamente sbagliato nel semplicemente restituire un DTO completo dell'azienda (dove ragionevole) e poi lasciare che la vista scelga i campi che sono interessati. Si tratta di trovare un equilibrio tra l'implementazione di un'adeguata separazione delle preoccupazioni ed evitare l'eccessiva ingegnerizzazione e la ripetizione inutile.
Flater

Questo ha senso per me. Molte grazie. Flater.
Arash,

Un'altra domanda. Hai detto "oggetto business e un oggetto vista". Pensavo che entrambi fossero uguali. Quando ho cercato, mi sono reso conto che Business Object ha un significato generale da confrontare con View Object . Ma Business Object dovrebbe essere derivato dal caso d'uso e Entity Object dovrebbe essere derivato dal modello dati, quindi sono diversi, vero? potresti spiegarlo un po ', per favore?
Arash,

@Arash: la differenza tra ciò che si chiama un "oggetto business" e un "oggetto vista" è il contesto . Per noi umani, questa distinzione conta per capire le cose correttamente. Ma il compilatore (e per estensione il linguaggio stesso) non vede una differenza tecnica tra di loro. Quando dico che sono uguali, intendo dal punto di vista tecnico. Entrambi sono solo una classe con proprietà destinate a contenere dati e ad essere passate in giro. A tal proposito, non vi è alcuna differenza tra loro.
Flater,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.