¿Debo asignar un DTO hacia/desde una entidad de dominio tanto en el lado del cliente como en el del servidor?

StackOverflow https://stackoverflow.com/questions/2201150

Pregunta

Tengo un modelo de dominio rico, donde la mayoría de las clases tienen algún comportamiento y algunas propiedades que se calculan o exponen las propiedades de los objetos miembro (es decir, que los valores de estas propiedades nunca persisten).

Mi cliente habla con el servidor solo a través de WCF.

Como tal, para cada entidad de dominio, tengo un DTO correspondiente (una representación simple que contiene solo datos), así como una clase de mapeador que implementa DtoMapper<DTO,Entity> y puede convertir una entidad a su equivalente DTO o viceversa a través de una puerta de enlace estática:

var employee = Map<Employee>.from_dto<EmployeeDto>();

El lado del servidor de esta aplicación tiene que ver principalmente con la persistencia, donde mis DTO provienen del servicio WCF, se deserializan y luego un ORM arbitrario los persiste en la base de datos, o llega una solicitud de consulta desde WCF y el ORM ejecuta esa consulta en la base de datos y devuelve objetos para ser serializados y enviados de vuelta por WCF.

Ante este escenario, ¿Tiene algún sentido asignar mi almacén de persistencia a las entidades del dominio, o debería simplemente asignarlo directamente a los DTO?

Si uso entidades de dominio, el flujo sería

  1. objeto de solicitud de cliente
  2. WCF transmite la solicitud al servidor
  3. ORM consulta la base de datos y devuelve entidades de dominio
  4. entidades de dominio transformadas en DTO por mapper
  5. WCF serializa DTO y regresa al cliente
  6. el cliente deserializa DTO
  7. DTO transformado en entidad de dominio por mapper
  8. modelos de vista creados, etc.

similar en el viaje de regreso

Si asigno directamente a DTO, puedo eliminar un mapeo por objeto, por solicitud.¿Qué pierdo al hacer esto?

Lo único que me viene a la mente es otra oportunidad para validar antes de insertar/actualizar, porque no tengo ninguna garantía de que el DTO haya estado alguna vez sujeto a validación o incluso existiera como una entidad de dominio antes de ser enviado a través de la red, y supongo que una oportunidad para validar al seleccionar (si otro proceso podría haber puesto valores no válidos en la base de datos).¿Hay otras razones?¿Son estas razones suficientes para justificar los pasos de mapeo adicionales?

editar:

Dije "ORM arbitrario" arriba, y quiero que las cosas sean lo más independientes posible de ORM y persistencia, pero si tiene algo especial que agregar que sea específico de NHibernate, hágalo por todos los medios.

¿Fue útil?

Solución

Yo personalmente recomiendo el mantenimiento de su asignación en el lado del servidor. Es probable que haya hecho un montón de trabajo la construcción de su diseño hasta el punto en que es ahora; no desperdiciarlo.

Considere lo que es un servicio web. No es meramente una abstracción sobre su ORM; se trata de un contrato . Es una API pública para sus clientes, tanto internos como externos.

Una API pública debe tener poca o ninguna razón para cambiar. Casi cualquier cambio a una API, además de la adición de nuevos tipos y métodos, es un cambio importante. Sin embargo, su modelo de dominio no va a ser tan estricto. Tendrá que cambiar de vez en cuando a medida que agrega nuevas funciones o descubrir defectos en el diseño original. ¿Quieres ser capaz de asegurar que los cambios en su modelo interno no causan cambios en cascada a través de un contrato de servicio.

En realidad es una práctica común (no voy a insultar a los lectores con la frase "las mejores prácticas") para crear clases Request y Response específicos para cada mensaje por una razón similar; se hace mucho más fácil de ampliar la capacidad de los servicios y métodos existentes sin que sean cambios de ruptura.

Los clientes probablemente no quieren el mismo modelo exacto que se utiliza internamente en el servicio. Si usted es su único cliente, entonces tal vez esto parece transparente, pero si tiene clientes externos y han visto cuán lejos de su interpretación de su sistema puede ser a menudo, entonces vas a entender el valor de no permitir que su modelo perfecto a la fuga fuera de los confines de la API del servicio.


Y a veces, ni siquiera es posible para enviar el modelo a través de la API. Hay muchas razones por las que esto puede ocurrir:

  • ciclos en el gráfico de objetos. Perfectamente bien en la programación orientada a objetos; desastrosa en la serialización. Al final tener que tomar decisiones permanentes dolorosas sobre el que la "dirección" del gráfico debe ser serializado en. Por otra parte, si se utiliza un DTO, puede serializar en la dirección que desea, lo que encaje con la tarea en cuestión.

  • El intento de utilizar ciertos tipos de mecanismos de herencia sobre SOAP / REST puede ser un kludge en el mejor. El serializador XML de estilo antiguo, al menos, apoya xs:choice; DataContract no lo hace, y no voy a discutir por razón de ser, pero basta con decir que es probable que tenga algún polimorfismo en el modelo de dominio rico y es maldito casi imposible de canalizar que a través del servicio web.

  • La carga diferida / diferido, lo que es probable que haga uso de si se utiliza un ORM. Es suficiente hacer incómoda asegurarse de que se serializado adecuadamente - por ejemplo, el uso de LINQ to SQL entidades, WCF no incluso desencadenar el cargador flojo, sólo va a poner null en la materia, salvo que se carga manualmente - pero el problema se agrava para los datos que vienen de vuelta en algo tan simple como un List<T> de auto-propiedad que ha inicializado en el constructor -. bastante común en un modelo de dominio - simplemente no funciona en WCF, ya que no invoca su constructor. En lugar de ello hay que añadir un método [OnDeserializing] inicializador, y realmente no quieren el desorden de su modelo de dominio con esta basura.

  • También he dado cuenta de la observación paréntesis que utiliza NHibernate. Consideran que las interfaces como IList<T> no pueden ser serializados en todo un servicio web! Si utiliza clases POCO con NHibernate, como la mayoría de nosotros, entonces esto simplemente no va a funcionar, y punto.


También habrá probablemente muchos casos en que el modelo de dominio interno simplemente no coincide con las necesidades del cliente, y que no tiene sentido cambiar su modelo de dominio de atender a esas necesidades. Como ejemplo de esto, vamos a tomar algo tan simple como una factura. Tiene que mostrar:

  • Información sobre la cuenta (número de cuenta, nombre, etc.)
  • los datos (número de factura, fecha, fecha de vencimiento, e-factura específicatc.)
  • información de A / R-nivel (saldo anterior, cargos por mora, nuevo equilibrio)
  • Información sobre el producto o servicio para todo en la factura;
  • etc.

Esto probablemente encaja bien dentro de un modelo de dominio. Pero ¿y si el cliente quiere ejecutar un informe que muestra 1200 de estas facturas? Una especie de informe de conciliación?

Esto es una mierda para la serialización. Ahora se va a enviar facturas de 1200 con los mismos los datos serializados siendo una y otra vez - mismas cuentas, los mismos productos, el mismo tipo A / R. Internamente, su aplicación es hacer el seguimiento de todos los enlaces; conoce la factura # 35 y # 45 son de factura para el mismo cliente y por lo tanto comparten una referencia Customer; toda esta información se pierde al serialización y se acaba de enviar una ridícula cantidad de datos redundantes.

Lo que realmente quiere es enviar un informe personalizado que incluye:

  • Todas las cuentas incluido en el informe, y su A / R;
  • Todos los productos incluidos en el informe;
  • Todas las facturas, con el producto y la ID de cuenta única.

Es necesario para llevar a cabo la "normalización" adicional en sus datos de salida antes de enviarlo al cliente si desea evitar la redundancia masiva. Esto favorece fuertemente el enfoque DTO; No tiene sentido tener esta estructura en su modelo de dominio debido a que su modelo de dominio ya se encarga de redundancias, a su manera.

Espero que esos son suficientes ejemplos y suficiente razón para convencer a mantener sus asignaciones de dominio <-> Contrato de Servicio intacta. Usted ha hecho absolutamente lo correcto hasta ahora, tiene un gran diseño, y sería una vergüenza para negar todo ese esfuerzo en favor de algo que podría llevar a grandes dolores de cabeza más adelante.

Otros consejos

Se le debe asignar los dtos en el lado del cliente de todos modos, así que, por simetría, es mejor hacer la aplicación inversa en el lado del servidor. De esta manera aislar sus conversiones en capas de abstracción bien separados.

capas de abstracción son buenos no sólo para las validaciones, sino para aislar su código de cambios por debajo / por encima de ella, y hacer que el código sea más comprobable y con menos repeticiones.

Además, a menos que note un gran cuello de botella en la conversión adicional, recuerde: la optimización temprana es la raíz de todo mal. :)

Definitivamente debe mantener las entidades de su dominio separadas de las de su DTO, ya que son preocupaciones diferentes.Los DTO suelen ser modelos heriacales que se autodescriben, en los que, por otro lado, las entidades de su dominio encapsulan su lógica empresarial y tienen mucho comportamiento asociado.

Dicho esto, ¿no estoy seguro de dónde está el mapeo adicional?¿Recupera los datos utilizando su ORM (también conocido como entidades de dominio) y asigna esos objetos a sus DTO para que solo haya 1 asignación allí?Por cierto, si aún no lo estás, usa algo como asignador automático para hacer el tedioso mapeo por usted.

Estos mismos DTO luego se deserializan en el cliente y desde allí puede asignarlos directamente a sus UIViewModels.Entonces el panorama general se parece a:

  • El cliente solicita entidad por ID del servicio WCF
  • El servicio WCF obtiene la entidad del repositorio/ORM
  • Utiliza un AutoMapper para asignar de la entidad al DTO
  • El cliente recibe DTO
  • Utiliza un AutoMapper para asignar al UI ViewModel
  • UIViewModel está vinculado a la GUI

Cuando usted dice que la aplicación del lado del servidor es "sobre todo" acerca de la persistencia, creo que esta es la clave que hay que pensar. ¿Hay realmente un modelo de dominio del servidor que requiere algo de inteligencia en torno a los datos que reciba o no el servicio WCF puramente actuar como puerta de enlace entre su modelo de dominio y el almacén de datos?

Además, considere si su DTO está diseñado para el dominio del cliente.
Es este el único dominio de cliente que necesita acceso a ese almacén de datos a través de su servicio?
Son los dtos del lado del servidor flexible o de grano grueso suficiente para servir a un dominio de aplicación diferente?
Si no es así, entonces es probable que vale la pena el esfuerzo para mantener las implementaciones de interfaces externas abstraídas.

  

(DB-> ORM-> EmployeeEntity-> Client1DTOAssembler-> Client1EmployeeDTO).

Tenemos una aplicación similar, donde un servicio WCF actúa principalmente como una puerta de entrada al almacén de datos persistente.

En nuestro caso, nuestro cliente y el servidor no vuelva a utilizar el ensamblaje que contiene "dtos." Esto nos da la oportunidad de simplemente agregar código a las clases parciales generadas por la referencia de servicio, lo que a menudo son capaces de utilizar un DTO tal y como está en el lado del cliente y tratarlo como un objeto de dominio. Otras veces pueden tener objetos del lado del cliente de sólo dominio que sirven como fachadas a un montón de objetos persistentes que obtuvimos del servicio WCF.

Cuando se piensa en los comportamientos y las propiedades calculadas que sus objetos de dominio tienen, la cantidad de solapamiento es allí, realmente entre el cliente y el servidor? En nuestro caso, se determinó que la división de las responsabilidades entre el cliente y el servidor significaba que había muy poco, en su caso, el código que tenía que estar presente (y exactamente el mismo) tanto en el cliente y el servidor.

Para responder a sus preguntas directamente, si su objetivo es permanecer completamente persistencia agnóstico, sin duda el mapa de su tienda de empeño de sus objetos de dominio y luego asignar a dtos. Hay demasiadas implementaciones de persistencia que pueden sangrar en sus objetos y complican su utilización como WCF dtos.

En el lado del cliente, puede que no sea necesario hacer una asignación adicional si sólo puede decorar o aumentar sus dtos y eso es una solución bastante simple.

Su arquitectura parece bastante bien pensado. Mi instinto de sentido es, si ya ha decidido reducir los objetos a DTO de enviarlos a través de WCF, y que actualmente no tienen una necesidad de funcionalidad objeto adicional en el lado del servidor, por qué no mantener las cosas simples y mapa su tienda persistencia directamente a los dtos.

¿Qué se pierde? No creo que realmente se pierde nada. Eres la arquitectura es limpia y sencilla. Si se decide en el futuro que hay una nueva necesidad de la funcionalidad más rica en el lado del servidor, puede volver a factor de siempre en ese punto para volver a crear las entidades del dominio allí.

Me gusta que sea sencillo y re-factor de que sea necesario más adelante, trate de evitar la cosa optimización prematura, etc.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top