2009-03-24 19 views
59

He visto muchas preguntas relacionadas con la asignación de DTO a objetos de dominio, pero no creo que hayan respondido mi pregunta. He usado muchos métodos antes y tengo mis propias opiniones, pero estoy buscando algo un poco más concreto.¿Mejores prácticas para asignar DTO al objeto de dominio?

La situación:

Tenemos muchos objetos de dominio. Estamos utilizando un modelo de CSLA para que nuestros objetos de dominio puedan ser bastante complejos y que tengan su propio acceso a los datos. No quiere pasar esto por el cable. Vamos a escribir algunos servicios nuevos que devolverán datos en varios formatos (.Net, JSON, etc.). Por esto (y por otros motivos), también estamos creando un objeto de transferencia de datos delgado para pasar por el cable.

Mi pregunta es ¿cómo se deben conectar el DTO y el objeto de dominio?

Mi primera reacción es usar Fowler, DTO pattern-type solution. He visto esto muchas veces y me parece bien. El objeto de dominio no contiene ninguna referencia al DTO. Se llama a una entidad externa (un "asignador" o "ensamblador") para crear un DTO a partir de un objeto de dominio. Normalmente hay un ORM en el lado del objeto del dominio. La desventaja de esto es que el "mapeador" tiende a ser extremadamente complejo para cualquier situación real y puede ser muy frágil.

Otra idea presentada es que el objeto de dominio "contenga" el DTO, ya que es solo un objeto de datos pobre. Las propiedades del objeto de dominio hacen referencia internamente a las propiedades de DTO y pueden simplemente devolver el DTO si así lo solicita. No veo problemas con esto, pero se siente mal. He visto algunos artículos donde las personas que usan NHibernate parecen usar este método.

¿Hay otras formas? ¿Vale la pena usar una de las formas anteriores? Si es así o no, ¿por qué?

Gracias por cualquier idea con anticipación.

+2

El automapper parece interesante. He visto un montón de código antes de que lo hubiera reemplazado. Mi principal problema es que, si por alguna razón me voy a quedar con un montón de código de mapeo, preferiría tener el control sobre mí mismo. –

+2

Cuando pasamos de DTO _to_ Domain Objects, esa asignación es 100% manual. Es un problema mucho más difícil de resolver, ya que tratamos de mantener nuestros objetos de dominio basados ​​en operaciones, en lugar de solo contenedores de datos. Ir a un DTO, es un problema fácil de resolver. –

+0

Estoy de acuerdo en que es incorrecto que el objeto de dominio no tenga conocimiento del objeto dto. Si bien pueden estar relacionados en este caso, su propósito es completamente independiente (los dtos generalmente están hechos para el propósito) y usted estaría creando una dependencia innecesaria. – Sinaesthetic

Respuesta

33

Una ventaja de tener un asignador que se encuentra entre su dominio y su DTO no es tan visible cuando solo admite una sola asignación, pero a medida que aumenta el número de asignaciones, tener ese código aislado del dominio ayuda a mantener el dominio más simple y más delgado. No abarrotarás tu dominio con mucho peso extra.

Personalmente, trato de mantener el mapeo fuera de las entidades de mi dominio y asumo la responsabilidad en lo que llamo "Capa de administrador/servicio". Esta es una capa que se encuentra entre la aplicación y el (los) repositorio (es) y proporciona lógica de negocios como la coordinación del flujo de trabajo (si modifica A, también deberá modificar B para que el servicio A funcione con el servicio B).

Si tuviera muchos posibles formatos de finalización, podría considerar la creación de un formateador enchufable que pudiera usar el patrón Visitor, por ejemplo para transformar mis entidades, pero aún no he encontrado la necesidad de nada para este complejo.

22

Puede utilizar un Automapper como the one written by Jimmy Bogard que no tiene conexión entre los objetos y se basa en las convenciones de nomenclatura que se cumplen.

+3

Automapper podría provocar la exposición accidental de propiedades -> agujero de seguridad. Sería mejor decir explícitamente qué debería exponerse como DTO. – deamon

+4

@deamon: una preocupación válida, pero también lo son los errores (y posibles agujeros de seguridad debido a la supervisión humana) que se pueden crear al escribir todo el código de mapeo pegajoso. Iré por la carretera automágica y manejaré el 5% usando su función de mapeo personalizado. – Merritt

+0

@deamon: ¿no puedes hacer la asignación condicional de esas propiedades que no debes exponer? ¿Pensando AutoMapper maneja ese escenario? –

1

Otra posible solución: http://glue.codeplex.com.

Características:

  • mapeo bidireccional
  • mapeo automático
  • Correspondencia entre diferentes tipos
  • anidado de mapeo y aplanamiento
  • listas y matrices
  • Verificación de las relaciones
  • Prueba las mapeo
  • propiedades, campos y métodos
5

Utilizamos plantillas T4 para crear las clases de mapeo.

Pro's - código legible para humanos disponible en tiempo de compilación, más rápido que un mapeador de tiempo de ejecución. 100% de control sobre el código (puede usar métodos parciales/patrón de plantilla para extender la funcionalidad en una base ad-hoc)

Con - excluyendo ciertas propiedades, colecciones de objetos de dominio, etc., aprendiendo la sintaxis T4.

0

Puedo sugerir una herramienta que he creado y es de código abierto alojado en CodePlex: EntitiesToDTOs.

La asignación desde DTO a Entity y viceversa se implementa mediante métodos de extensión, estos componen el lado del Ensamblador de cada extremo.

Se termina con un código como:

Foo entity = new Foo(); 
FooDTO dto = entity.ToDTO(); 
entity = dto.ToEntity(); 

List<Foo> entityList = new List<Foo>(); 
List<FooDTO> dtoList = entityList.ToDTOs(); 
entityList = dtoList.ToEntities(); 
+0

esto es arquitectónicamente incorrecto porque haces DTO y entidades de dominio conscientes el uno del otro. – Raffaeu

+5

@Raffaeu No lo creo, ya que los métodos ToDTO/ToDTOs/ToEntity/ToEntities se definen como métodos de extensión que representan los Ensambladores. La lógica de convertir una Entidad a DTO y viceversa está en los métodos de extensión (Ensambladores), no en la Entidad/DTO de hecho. – kzfabi

+2

Si habla de "Ensamblador", impleméntelos de forma correcta. Haz que sean modulares, haz que sean fáciles de intercambiar, usa inyección de dependencia. No es necesario que el modelo de dominio conozca una conversión a DTO. Digamos que tengo 1 objeto de dominio pero 50 aplicaciones diferentes que usan el mismo dominio, cada una con su propia DTO. No vas a crear 50 extensiones. En su lugar, creará un servicio de aplicación para cada aplicación con el (los) ensamblador (s) necesarios que se inyectan como una dependencia en el servicio. –

1

¿Cómo ve usted para implementar un constructor dentro de la clase DTO que toma como parámetro un objeto de dominio?

Di ... Algo como esto

class DTO { 

    // attributes 

    public DTO (DomainObject domainObject) { 
      this.prop = domainObject.getProp(); 
    } 

    // methods 
} 
+6

Por favor, nunca hagas esto. No desea que su capa DTO tenga conocimiento o dependa de su capa de dominio. La ventaja del mapeo es que las capas inferiores pueden cambiarse fácilmente cambiando la asignación, o que las modificaciones en la capa inferior pueden ser controladas cambiando la asignación. Digamos que dtoA se asigna a domainObjectA hoy, pero mañana el requisito es que se aplique a domainObjectB. En su caso, debe modificar el objeto DTO, que es un gran no-no. Has perdido muchos beneficios del mapeador. –

+1

En primer lugar, ¡gracias! :RE. Así que @FrederikPrijck insertando una capa entre 'DTO' y' DomainObject', básicamente nos esforzamos este problema de la DTO depende del objeto de dominio, por lo que todo el "trabajo de construcción" se realiza en una capa intermedia (clase) llamada ' mapper', que depende tanto de DTO como de DomainObjects. Entonces, ¿cuál es el mejor enfoque, o en general recomiendan, para este asunto? Solo pregunto para asegurarme de que el punto fue entendido. – Victor

+4

Sí, la capa se llama "Ensamblador". Al utilizar una tercera capa para definir las asignaciones, permite la posibilidad de reemplazar fácilmente la capa de ensamblador por otra implementación (por ejemplo, eliminar Automapper y usar asignaciones manuales), que siempre es una mejor opción. La mejor manera de entenderlo es pensar dónde le daría el Objeto A, y otra persona le da el Objeto B. No tiene acceso a cada uno de esos objetos (solo dll), por lo que el mapeo solo puede hacerse creando un 3er. capa. Pero incluso si puede acceder a cualquiera de los objetos, las asignaciones siempre deben realizarse fuera, ya que no están relacionadas. –

0

Por qué no podemos hacerlo de esta manera?

class UserDTO { 
} 

class AdminDTO { 
} 

class DomainObject { 

// attributes 
public DomainObject(DTO dto) { 
     this.dto = dto; 
}  

// methods 
public function isActive() { 
     return (this.dto.getStatus() == 'ACTIVE') 
} 

public function isModeratorAdmin() { 
     return (this.dto.getAdminRole() == 'moderator') 
} 

} 


userdto = new UserDTO(); 
userdto.setStatus('ACTIVE'); 

obj = new DomainObject(userdto) 
if(obj.isActive()) { 
    //print active 
} 

admindto = new AdminDTO(); 
admindto.setAdminRole('moderator'); 

obj = new DomainObject(admindto) 
if(obj.isModeratorAdmin()) { 
    //print some thing 
} 

@FrederikPrijck (o) a alguien: Por favor sugerir. En el ejemplo anterior, DomainObject depende de DTO. De esta manera puedo evitar el código para hacer la asignación del dto < -> domainobject.

o la clase DomainObject puede extender la clase DTO?