Il est difficile d'établir les meilleures pratiques pour quelque chose d'aussi "flexible" ou abstrait qu'un DTO. Essentiellement, les DTO ne sont que des objets pour le transfert de données, mais selon la destination ou la raison du transfert, vous souhaiterez peut-être appliquer différentes "meilleures pratiques".
Je recommande de lire Patterns of Enterprise Application Architecture par Martin Fowler . Il y a tout un chapitre dédié aux modèles, où les DTO obtiennent une section vraiment détaillée.
À l'origine, ils étaient «conçus» pour être utilisés dans des appels à distance coûteux, où vous auriez probablement besoin de beaucoup de données provenant de différentes parties de votre logique; Les DTO feraient le transfert de données en un seul appel.
Selon l'auteur, les DTO n'étaient pas destinés à être utilisés dans des environnements locaux, mais certaines personnes en ont trouvé une utilisation. Habituellement, ils sont utilisés pour collecter des informations provenant de différents POCO dans une seule entité pour les interfaces graphiques, les API ou différentes couches.
Maintenant, avec l'héritage, la réutilisation du code ressemble plus à un effet secondaire de l'héritage qu'à son objectif principal; d'autre part, la composition est mise en œuvre avec la réutilisation du code comme objectif principal.
Certaines personnes recommandent d'utiliser la composition et l'héritage ensemble, en utilisant les forces des deux et en essayant d'atténuer leurs faiblesses. Ce qui suit fait partie de mon processus mental lors du choix ou de la création de nouveaux DTO, ou de toute nouvelle classe / objet d'ailleurs:
- J'utilise l'héritage avec des DTO à l'intérieur de la même couche ou du même contexte. Un DTO n'héritera jamais d'un POCO, un BLL DTO n'héritera jamais d'un DAL DTO, etc.
- Si je me retrouve à essayer de cacher un champ à un DTO, je vais refactoriser et peut-être utiliser la composition à la place.
- Si très peu de champs différents d'un DTO de base sont tout ce dont j'ai besoin, je les mettrai dans un DTO universel. Les DTO universels ne sont utilisés qu'en interne.
- Un POCO / DTO de base ne sera presque jamais utilisé pour une quelconque logique, de cette façon, la base ne répond qu'aux besoins de ses enfants. Si jamais j'ai besoin d'utiliser la base, j'évite d'ajouter tout nouveau champ que ses enfants n'utiliseront jamais.
Certaines d'entre elles ne sont peut-être pas les "meilleures" pratiques, elles fonctionnent assez bien pour les projets sur lesquels je travaille, mais vous devez vous rappeler qu'aucune taille ne convient à tous. Dans le cas du DTO universel, vous devez être prudent, mes signatures de méthodes ressemblent à ceci:
public void DoSomething(BaseDTO base) {
//Some code
}
Si l'une des méthodes a besoin de son propre DTO, je fais l'héritage et généralement le seul changement que je dois faire est le paramètre, bien que parfois je doive creuser plus profondément pour des cas spécifiques.
D'après vos commentaires, je comprends que vous utilisez des DTO imbriqués. Si vos DTO imbriqués se composent uniquement d'une liste d'autres DTO, je pense que la meilleure chose à faire est de déballer la liste.
Selon la quantité de données que vous devez afficher ou utiliser, il peut être judicieux de créer de nouveaux DTO qui limitent les données; par exemple, si votre UserDTO a beaucoup de champs et que vous n'avez besoin que de 1 ou 2, il peut être préférable d'avoir un DTO avec uniquement ces champs. La définition de la couche, du contexte, de l'utilisation et de l'utilité d'un DTO aidera beaucoup lors de sa conception.