Es difícil establecer las mejores prácticas para algo tan "flexible" o abstracto como un DTO. Esencialmente, los DTO son solo objetos para la transferencia de datos, pero dependiendo del destino o el motivo de la transferencia, es posible que desee aplicar diferentes "mejores prácticas".
Recomiendo leer Patrones de arquitectura de aplicaciones empresariales de Martin Fowler . Hay un capítulo completo dedicado a los patrones, donde los DTO obtienen una sección realmente detallada.
Originalmente, fueron "diseñados" para ser usados en costosas llamadas remotas, donde probablemente necesitaría muchos datos de diferentes partes de su lógica; Los DTO harían la transferencia de datos en una sola llamada.
Según el autor, los DTO no estaban destinados a ser utilizados en entornos locales, pero algunas personas encontraron un uso para ellos. Por lo general, se utilizan para recopilar información de diferentes POCO en una sola entidad para GUI, API o diferentes capas.
Ahora, con la herencia, la reutilización del código es más un efecto secundario de la herencia que su objetivo principal; La composición, por otro lado, se implementa con la reutilización del código como objetivo principal.
Algunas personas recomiendan el uso de la composición y la herencia juntos, utilizando las fortalezas de ambos y tratando de mitigar sus debilidades. Lo siguiente es parte de mi proceso mental al elegir o crear nuevos DTO, o cualquier clase / objeto nuevo para el caso:
- Utilizo la herencia con DTO dentro de la misma capa o el mismo contexto. Un DTO nunca heredará de un POCO, un BLL DTO nunca heredará de un DAL DTO, etc.
- Si me encuentro tratando de ocultar un campo de un DTO, refactorizaré y tal vez use composición en su lugar.
- Si muy pocos campos diferentes de un DTO base es todo lo que necesito, los pondré en un DTO universal. Los DTO universales se usan solo internamente.
- Un POCO / DTO base casi nunca se usará para ninguna lógica, de esa manera la base responde solo a las necesidades de sus hijos. Si alguna vez necesito usar la base, evito agregar cualquier campo nuevo que sus hijos nunca usen.
Algunos de ellos tal vez no sean las "mejores" prácticas, funcionan bastante bien para los proyectos en los que he estado trabajando, pero debe recordar que no hay tamaño para todos. En el caso del DTO universal, debe tener cuidado, las firmas de mis métodos se ven así:
public void DoSomething(BaseDTO base) {
//Some code
}
Si alguno de los métodos alguna vez necesita su propio DTO, heredo y, por lo general, el único cambio que necesito hacer es el parámetro, aunque a veces necesito profundizar en casos específicos.
De sus comentarios, entiendo que está utilizando DTO anidados. Si sus DTO anidados consisten solo en una lista de otros DTO, creo que lo mejor es desenvolver la lista.
Dependiendo de la cantidad de datos que necesite mostrar o trabajar, puede ser una buena idea crear nuevos DTO que limiten los datos; por ejemplo, si su UserDTO tiene muchos campos y solo necesita 1 o 2, puede ser mejor tener un DTO con solo esos campos. Definir la capa, el contexto, el uso y la utilidad de un DTO ayudará mucho al diseñarlo.