È difficile stabilire le migliori pratiche per qualcosa di "flessibile" o astratto come un DTO. Essenzialmente, i DTO sono solo oggetti per il trasferimento dei dati ma, a seconda della destinazione o del motivo del trasferimento, potresti voler applicare diverse "best practice".
Raccomando di leggere Modelli di Enterprise Application Architecture
di Martin Fowler . C'è un intero capitolo dedicato ai pattern, in cui le DTO hanno una sezione molto dettagliata.
Originariamente, erano "progettati" per essere usati in costose chiamate remote, dove probabilmente avresti bisogno di molti dati provenienti da parti diverse della tua logica; Le DTO effettuano il trasferimento di dati in un'unica chiamata.
Secondo l'autore, le DTO non erano pensate per essere utilizzate in ambienti locali, ma alcune persone ne hanno trovato un uso. Solitamente vengono utilizzati per raccogliere informazioni da diversi POCO in un'unica entità per GUI, API o livelli diversi.
Ora, con l'ereditarietà, il riutilizzo del codice è più simile a un effetto collaterale dell'eredità piuttosto che al suo obiettivo principale; la composizione, d'altra parte, è implementata con il riutilizzo del codice come obiettivo principale.
Alcune persone raccomandano l'uso della composizione e dell'eredità insieme, usando i punti di forza di entrambi e cercando di mitigare le loro debolezze. Quanto segue fa parte del mio processo mentale quando scelgo o creo nuovi DTO o qualsiasi nuova classe / oggetto per quella materia:
- Uso l'ereditarietà con DTO all'interno dello stesso livello o dello stesso contesto. Un DTO non erediterà mai da un POCO, un DTO BLL non erediterà mai un DTO DAL, ecc.
- Se mi trovo a cercare di nascondere un campo da un DTO, mi rifatterò e forse userò la composizione.
- Se pochissimi campi diversi da un DTO di base sono tutto ciò di cui ho bisogno, li inserirò in un DTO universale. I DTO universali sono utilizzati solo internamente.
- Un POCO / DTO di base non verrà quasi mai utilizzato per nessuna logica, in questo modo la base risponde solo alle esigenze dei suoi figli. Se dovessi mai usare la base, evito di aggiungere nuovi campi che i bambini non useranno mai.
Alcuni di loro forse non sono le migliori pratiche, funzionano piuttosto bene per i progetti su cui ho lavorato, ma è necessario ricordare che nessuna taglia si adatta a tutti. Nel caso del DTO universale dovresti stare attento, i miei metodi firme hanno questo aspetto:
public void DoSomething(BaseDTO base) {
//Some code
}
Se uno qualsiasi dei metodi ha mai bisogno del proprio DTO, faccio ereditarietà e di solito l'unico cambiamento che devo apportare è il parametro, anche se a volte ho bisogno di scavare più a fondo per casi specifici.
Dai tuoi commenti ho capito che stai usando DTO annidati. Se i tuoi DTO nidificati consistono solo in un elenco di altri DTO, penso che la cosa migliore da fare sia scartare l'elenco.
A seconda della quantità di dati che devi mostrare o di lavorare, potrebbe essere una buona idea creare nuovi DTO che limitino i dati; per esempio, se il tuo UserDTO ha molti campi e hai solo bisogno di 1 o 2, potrebbe essere meglio avere un DTO con solo quei campi. Definire il livello, il contesto, l'utilizzo e l'utilità di un DTO sarà di grande aiuto durante la progettazione.