Cada vez que un desarrollador pregunta "¿cuál es el punto de hacer esto?", Lo que realmente quieren decir es "No veo ningún caso de uso en el que hacerlo proporcione un beneficio". Con ese fin, déjame mostrarte algunos ejemplos.
Todos los ejemplos se basarán en este modelo de datos simple:
Una Person
entidad tiene cinco propiedades:Id, FirstName, LastName, Age, CityId
Y puede suponer que la aplicación utiliza estos datos de muchas maneras diferentes (informes, formularios, ventanas emergentes, ...).
Toda la aplicación ya existe. Todo lo que menciono es un cambio en la base de código existente. Esto es importante para recordar.
Ejemplo 1 - Cambiar la estructura de datos subyacente - Sin DTO
Los requisitos han cambiado. La edad de la persona debe recuperarse dinámicamente de la base de datos del gobierno (supongamos que se basa en su nombre y apellido).
Como ya no necesita almacenar el Age
valor localmente, por lo tanto, debe eliminarse de la Person
entidad. Aquí es importante darse cuenta de que la entidad representa los datos de la base de datos , y nada más. Si no está en la base de datos, no está en la entidad.
Cuando recupera la antigüedad del servicio web del gobierno, se almacenará en un objeto diferente (o int).
Pero su interfaz aún muestra una edad. Todas las vistas se han configurado para usar la Person.Age
propiedad, que ahora ya no existe. Se presenta un problema: todas las vistas que se refieren a la Age
de una persona necesitan ser reparadas .
Ejemplo 2 - Cambiar la estructura de datos subyacente - Con DTO
En el antiguo sistema, también hay PersonDTO
entidades con los mismos cinco propiedades: Id, FirstName, LastName, Age, CityId
. Después de recuperar a Person
, la capa de servicio lo convierte en ay PersonDTO
luego lo devuelve.
Pero ahora, los requisitos han cambiado. La edad de la persona debe recuperarse dinámicamente de la base de datos del gobierno (supongamos que se basa en su nombre y apellido).
Como ya no necesita almacenar el Age
valor localmente, por lo tanto, debe eliminarse de la Person
entidad. Aquí es importante darse cuenta de que la entidad representa los datos de la base de datos , y nada más. Si no está en la base de datos, no está en la entidad.
Sin embargo, dado que tiene un intermediario PersonDTO
, es importante ver que esta clase pueda conservar la Age
propiedad. La capa de servicio buscará Person
, la convertirá en a PersonDTO
, luego también buscará la edad de la persona del servicio web del gobierno, almacenará ese valor PersonDTO.Age
y pasará ese objeto.
La parte importante aquí es que cualquiera que use la capa de servicio no ve una diferencia entre el sistema antiguo y el nuevo . Esto incluye su interfaz. En el sistema anterior, recibía un PersonDTO
objeto completo . Y en el nuevo sistema, todavía recibe un PersonDTO
objeto completo . Las vistas no necesitan ser actualizadas .
Esto es lo que queremos decir cuando usamos la frase separación de inquietudes : hay dos inquietudes diferentes (almacenar los datos en la base de datos, presentar los datos a la interfaz) y necesitan un tipo de datos diferente cada uno. Incluso si esos dos tipos de datos contienen los mismos datos en este momento, eso podría cambiar en el futuro.
En el ejemplo dado, Age
hay una diferencia entre los dos tipos de datos: Person
(la entidad de la base de datos) no necesita un Age
, pero PersonDTO
(el tipo de datos frontend) sí lo necesita.
Al separar las preocupaciones (= crear tipos de datos separados) desde el principio, la base de código es mucho más resistente a los cambios realizados en el modelo de datos.
Podría argumentar que tener un objeto DTO, cuando se agrega una nueva columna a la base de datos, significa que tiene que hacer un doble trabajo, agregando la propiedad tanto en la entidad como en el DTO. Eso es técnicamente correcto. Se requiere un poco de esfuerzo extra para mantener dos clases en lugar de una.
Sin embargo, debe comparar el esfuerzo requerido. Cuando se agregan una o más columnas nuevas, copiar / pegar algunas propiedades no toma tanto tiempo. Cuando el modelo de datos cambia estructuralmente, tener que cambiar la interfaz, posiblemente de formas que solo causen errores en tiempo de ejecución (y no en tiempo de compilación), requiere un esfuerzo considerablemente mayor y requiere que el desarrollador (s) busque errores.
Podría darte más ejemplos, pero el principio siempre será el mismo.
Para resumir
- Las responsabilidades separadas (inquietudes) deben trabajar por separado una de la otra. No deben compartir ningún recurso, como clases de datos (p
Person
. Ej. )
- El hecho de que una entidad y su DTO tengan las mismas propiedades no significa que deba fusionarlas en la misma entidad. No cortes esquinas.
- Como un ejemplo más descarado, digamos que nuestra base de datos contiene países, canciones y personas. Todas estas entidades tienen a
Name
. Pero el hecho de que todos tengan una Name
propiedad no significa que debamos hacer que hereden de una EntityWithName
clase base compartida . Las diferentes Name
propiedades no tienen una relación significativa.
- Si una de las propiedades vez cambia (por ejemplo, una canción del
Name
consigue a llamarse a Title
, o una persona recibe un FirstName
e LastName
), que se tendrá que invertir un mayor esfuerzo deshacer la herencia , que ni siquiera necesita en primer lugar .
- Aunque no es tan evidente, su argumento de que no necesita un DTO cuando tiene una entidad es el mismo. Estás viendo el ahora , pero no te estás preparando para ningún cambio futuro. SI la entidad y el DTO son exactamente iguales, y SI puede garantizar que nunca habrá cambios en el modelo de datos; entonces tiene razón en que puede omitir el DTO. Pero la cuestión es que nunca puede garantizar que el modelo de datos nunca cambie.
- Las buenas prácticas no siempre dan resultado de inmediato. Puede comenzar a dar sus frutos en el futuro, cuando necesite volver a visitar una aplicación anterior.
- La principal causa de muerte de las bases de código existentes es dejar caer la calidad del código, lo que dificulta continuamente el mantenimiento de la base de código, hasta que se convierte en un desorden inútil de código de espagueti que no se puede mantener.
- Las buenas prácticas, como la implementación de una separación de las preocupaciones con respecto al inicio, tienen como objetivo evitar esa pendiente resbaladiza de mal mantenimiento, a fin de mantener la base de código mantenible durante el mayor tiempo posible.
Como regla general para considerar las preocupaciones de separación, piense de esta manera:
Suponga que cada inquietud (la interfaz de usuario, la base de datos, la lógica) es manejada por una persona diferente en una ubicación diferente. Solo pueden comunicarse por correo electrónico.
En una base de código bien separada, un cambio a una preocupación particular solo deberá ser manejado por una persona:
- Cambiar la interfaz de usuario solo implica el desarrollo de la interfaz de usuario.
- Cambiar el método de almacenamiento de datos solo implica el desarrollo de la base de datos.
- Cambiar la lógica de negocios solo involucra al desarrollador de negocios.
Si todos estos desarrolladores estuvieran usando la misma Person
entidad, y se hiciera un cambio menor en la entidad, todos tendrían que participar en el proceso.
Pero al usar clases de datos separadas para cada capa, ese problema no es tan frecuente:
- Mientras el desarrollador de la base de datos pueda devolver un
PersonDTO
objeto válido , al desarrollador de negocios y de la interfaz de usuario no le importa que haya cambiado la forma en que se almacenan / recuperan los datos.
- Mientras el desarrollador de negocios almacene los datos en la base de datos y proporcione los datos necesarios a la interfaz, a los desarrolladores de la base de datos y de la interfaz de usuario no les importa si decidió volver a trabajar sus reglas de negocio.
- Siempre que la interfaz de usuario se pueda diseñar en función del `PersonViewModel, el desarrollador de la interfaz de usuario puede construir la interfaz de usuario como lo deseen. La base de datos y los desarrolladores de negocios no les importa cómo se hace, ya que no los afecta.
La frase clave aquí es que no les afecta . La implementación de una buena separación de preocupaciones busca minimizar el afectar (y por lo tanto tener que involucrar) a otras partes.
Por supuesto, algunos cambios importantes no pueden evitar incluir a más de una persona, por ejemplo, cuando se agrega una entidad completamente nueva a la base de datos. Pero no subestimes la cantidad de cambios menores que tienes que hacer durante la vida útil de una aplicación. Los cambios importantes son una minoría numérica.
What's the benefit of these conversions?
desacoplar el modelo de datos de persistencia del modelo de datos (representación) ofrecido a los consumidores. Los beneficios del desacoplamiento se han discutido ampliamente en SE. Sin embargo, el objetivo debajo de los DTO es reunir en una sola respuesta tanta información como se considere necesaria para que los clientes guarden llamadas al servidor. Lo que hace que la comunicación cliente-servidor sea más fluida.