Data Transfer Object (DTO) – Patrón de diseño

Data Transfer Object (DTO) – Patrón de diseño

Una de las problemáticas más comunes cuando desarrollamos aplicaciones, es diseñar la forma en que la información debe viajar desde la capa de servicios a las aplicaciones o capa de presentación, ya que muchas veces por desconocimiento o pereza, utilizamos las clases de entidades para retornar los datos, lo que ocasiona que retornemos más datos de los necesarios o incluso, tengamos que ir en más de una ocasión a la capa de servicios para recuperar los datos requeridos.

 

El patrón DTO tiene como finalidad de crear un objeto plano (POJO) con una serie de atributos que puedan ser enviados o recuperados del servidor en una sola invocación, de tal forma que un DTO puede contener información de múltiples fuentes o tablas y concentrarlas en una única clase simple.

DTO mapping
DTO mapping

 

En la imagen anterior podemos apreciar gráficamente como es que un DTO se conforma de una serie de atributos que puede o no, estar conformados por más de una fuente de datos. Para esto, el servidor obtiene la información de las tablas customer y address (izquierda) y realiza un mapping con el DTO (derecha). Adicional, la información puede ser pasada de un lado intacta como es el caso del id , fullName , country , address  y zipCode  o ser una derivada de más de un campo, como es el caso del fullName , el cual es la unión del firstname  y lastname .

Otra de las ventajas no tan claras en la imagen, es que nos permite omitir información que el usuario no requiere, como es el caso de password. No es solo que no lo requiere, sino que además podría ser una gran falla de seguridad está enviando los passwords, es por ello que en el DTO lo omitimos.

 

Características de un DTO

 

Si bien un DTO es simplemente un objeto plano, sí que tiene que cumplir algunas reglas para poder considerar que hemos creado un DTO correctamente implementado:

  • Solo lectura: Dado que el objetivo de un DTO es utilizarlo como un objeto de transferencia entre el cliente y el servidor, es importante evitar tener operaciones de negocio o métodos que realicen cálculos sobre los datos, es por ello que solo deberemos de tener los métodos GET y SET de los respectivos atributos del DTO.
  • Serializable: Es claro que, si los objetos tendrán que viajar por la red, deberán de poder ser serializables, pero no hablamos solamente de la clase en sí, sino que también todos los atributos que contenga el DTO deberán ser fácilmente serializables. Un error clásico en Java es, por ejemplo, crear atributos de tipo Date o Calendar para transmitir la fecha u hora, ya que estos no tienen una forma estándar para serializarse por ejemplo en Webservices o REST.

 

Entidades vs DTO

 

Un error muy frecuente entre programadores inexpertos es el hecho de utilizar las clases de Entidad para utilizarlos para la transmisión de datos entre el cliente y el servidor. Solo para entrar en contexto, las entidades son clases que representa al modelo de datos, o mapea directamente contra una tabla de la base de datos. Dicho esto, las entidades son clases que fueron diseñadas para mapear contra la base de datos, no para ser una vista para una pantalla o servicio determinado, lo que provoca que muchos de los campos no puedan ser serializables, no contengan todos los campos necesarios un servicio, ya sea que tengan de más o de menos.

El hecho de que las entidades no contengan todos los atributos necesarios o que no sean serializables trae otros problemas, como la necesidad de agregaras más atributos a las entidades con el único objetivo de poder cubrir los requerimientos de transferencia de datos, dejando de lado el verdadero propósito de la entidad, que es únicamente mapear contra la base de datos, lo que va llevando lentamente a ir creando una mescla entre Entidad y DTO.

 

Introducción a los patrones de diseño - un enfoque práctico
¿Quieres aprender más patrones como este? te invito a ver mi libro de “Introducción a los patrones de diseño” un libro donde explico los principales patrones de diseño mediante ejemplos del mundo real, olvídate de aprender los ejemplos típicos de internet, como crear una pizza, animales que ladre o figuras geométricas.

 

DTO en el mundo real

 

Para comprender mejor como es que se utilizan los DTO, vamos a realizar un análisis con un ejemplo de un servicio que recupera los datos todos los datos de los clientes. Para esto, veamos como quedarían las Entidades utilizando el API de JPA de Java.

 

Entidad Customer

package dtopattern;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="customers")
public class Customer {
	@Id
	@GeneratedValue(strategy= GenerationType.IDENTITY)
	private Long id;
	@Column(name="firstname")
	private String firstname;
	@Column(name="lastname")
	private String lastname;
	@Column(name="password")
	private String password;
	
	/** GET and SET */
}

Entidad Address

package dtopattern;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name="address")
public class Address {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Long id;
	@ManyToMany()
	@JoinColumn(name="fk_customer")
	private Customer customer;
	@Column(name="country")
	private String country;
	@Column(name="address")
	private String address;
	@Column(name="zipcode")
	private String zipCode;
	
	
	/** GET and SET */
	
}

 

Por otro lado, tenemos el DTO que contiene los datos del cliente (Customer) y su dirección (Address).

package dtopattern;

import java.io.Serializable;

public class CustomerDTO implements Serializable{
	
	private Long id;
	private String FullName;
	private String country;
	private String Address;
	private String zipCode;
	
	
	/** GET and SET */
}

 

Finalmente, veamos cómo quedaría un servicio que aproveche las ventajas del patrón DTO para transmitir los datos:

package dtopattern;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("customers")
public class CustomerService {
	
	@GET
	@PathParam("{customerId}")
	private Response findCustomer(@PathParam("customerId") Long customerId) {
		Customer customer = customerDAO.findCustomerById(customerId); //Entity
		Address address = customerDAO.findAddressByCustomer(customerId); //Entity
		
		//Create dto
		CustomerDTO dto = new CustomerDTO();
		dto.setAddress(address.getAddress());
		dto.setCountry(address.getCountry());
		dto.setZipCode(address.getZipCode());
		dto.setFullName(customer.getFirstname() + " " + customer.getLastname());
		dto.setId(customer.getId());
		
		//Return DTO
		return Response.ok(dto, MediaType.APPLICATION_JSON).build();
	}
}

 

El ejemplo que acabamos de ver, corresponde a una implementación de un servicio REST utilizando el API JAX-RS de Java, el cual indica que existe un servicio GET en la url /customers/{customerID}, donde {customerId} corresponde al ID del cliente a buscar. En el servicio, realizamos la consulta del cliente y su dirección en dos pasos, para finalmente, mapear los datos de estas dos entidades en un simple DTO que será retornado.

 

Ponte a pensar, como le haríamos para obtener los datos del cliente y su dirección en una sola llamada al API REST sin ayuda de un DTO. Seguramente no podremos hacerlo en una sola llamada, si no que tendríamos que hacer dos consultas, una para recuperar el Cliente y otra para recuperar su dirección, o la otra alternativa y la peor de todas, sería modificar el Entidad Customer para agregar los campos que nos falte, a pesar de que la tabla no contenga esos campos.

 

 

Conclusiones

 

Como hemos podido demostrar, los DTO son un patrón muy efectivo para transmitir información entre un cliente y un servidor, pues permite crear estructuras de datos independientes de nuestro modelo de datos, lo que nos permite crear cuantas “vistas” sean necesarias de un conjunto de tablas u orígenes de datos. Además, nos permite controlar el formato, nombre y tipos de datos con los que transmitimos los datos para ajustarnos a un determinado requerimiento. Finalmente, si por alguna razón, el modelo de datos cambio (y con ello las entidades) el cliente no se afectará, pues seguirá recibiendo el mismo DTO.

30 thoughts to “Data Transfer Object (DTO) – Patrón de diseño”

  1. Habia leido varios articulos pero no me quedaba claro lo que es un DTO. Tu blog me ayudo entender lo que es y hace un DTO. El ejemplo de codigo me ayudo mucho. Great job!

      1. Pregunta, tal vez un poco tonta. Que tan recomendable es despachar la información desde un service, en lugar de tener un controller que se encargue de eso y a su vez utilize el service con la lógica que implementaste

        1. pues todo depende del nivel de arquitectura que quieras lograr, en aplicaciones pequeñas puede se recomendable hacerlo directamente desde el servicios para no hace un sobre ingeniería, pero si son aplicaciones muy grandes, quizás los controllers te puedan ayudar mas.

  2. Hola , una consulta, dices: “Ponte a pensar, como le haríamos para obtener los datos del cliente y su dirección en una sola llamada al API REST sin ayuda de un DTO. Seguramente no podremos hacerlo en una sola llamada, si no que tendríamos que hacer dos consultas,”

    Lo ideal no seria que la API exponga un servicio que te provea toda la data del cliente? Eso seria mas eficiente en términos de llamadas (Ya que haces solo una llamada de red) y términos de procesamiento en la DB (ya que la API haría un JOIN (Una sola consulta), y no dos)..

    1. hola Emiliano, creo que comprendiste mal el concepto, lo que quiero decir es que el DTO evita tener que hacer más llamadas al servidor, evitando como tu dices, que se hagan varias consultas en lugar de una. Sin el DTO estamos obligados a realizar varias consultas retornar toda la información, pero con el DTO podemos retornar toda la información en una sola llamada.

  3. Hola nien explicado, pero las clases Entidades son lo mismo que las clases DAO? por decir
    es lo mismo una clase llamada UserEntity vs UserDAO ? en c# se utiliza mucho la palabra Entity es correcto esta afirmación, si no lo es cúal es su diferencia ? saludos

    1. Hola Diego, en realidad son cosas diferentes, un Entidad es una clase que por lo general representa una tabla de la base de datos, es decir, por lo general tenemos una Entidad por tabla, la idea de la Entity es poder representar nuestros modelo relacional en objetos, y es allí donde entran las Entidades, por otra parte, un DAO es un patrón arquitectónico que tiene como objetivo encapsular la lógica de acceso a datos, es decir, mediante el DAO podemos consultar, guardar, actualizar o borrar. La idea del DAO es que encapsulemos la base de datos con la que estamos trabajando, de esta forma, podríamos (en teoría), cambiar de base de datos sin que el resto del sistema se ve afectado, además, el DAO trabaja con Entidades, es decir, le enviamos entidades para guardar, actualizar y borrar, y como resultado nos retorna Entidades, de esta forma, nos olvidamos del SQL o la DB NoSQL que estés utilizando.

  4. No sé si entendí bien . Solo sería para los GET ? En caso POST o PUT no se deberían usar .
    Vi en varios proyectos en Github que crean un DTO para actualizar y otro para crear aparte del GET. Pero eran ejemplos de CRUDs simples donde solo usaban datos para una tabla . No para dos o más como tú ejemplos que si son más útiles .

    Me gustaría saber más ,para no implementarlo mal. Agradecería tu ayuda .

    1. Hola Rasec, los DTO se pueden utilizar para cualquier operación, ya sea GET, PUT, DELETE, POST, la idea es hacer una sola llamada al servidor, ya sea en el request o en el response, y si, lo puedes utilizar para combinar información de varias tablas, de echo, es lo más normal hacerlo.

  5. Hola
    Me surgio la siguientes dudas:
    1.-Mencionas que solo se haria una peticion al servicio, pero esto podria hacerse tambien si llamas a la entiddad “Adress”.
    2.-Si necesitas obtener al usuario, lo puedes hacer tambien directamente desde la entidad Adress porque esta ya mapeada, sin necesidad de generar una especie de Entity-DTO
    3.-Ademas de la seguridad del ejemplo de la contraseña, en que afecta que una entidad se comunique con front por ejemplo JSF.

    Entiendo con esto que el uso del DTO es no dar otra funcionalidad a las Entidades que no sea el mapeo de la BD y usar entonces a los DTO para comunicar entre las diferentes capas del sistema.

    1. Hola Ivan, el problema principal de usar las Entitys es que estás atrapado en las estructura de las entidades para envíar datos, cualquier cambios de tipo de dato o formato, obliga a crear propiedades @Transient que distorsionan el uso de la Entidad, por otra parte, es común que cuando mandamos Entidades se enviemos datos de más, ya que las entidades están ligadas a otras entidades, lo que hace que por ejemplo, al mandar el Addres, termines mandando también el usuario ligado u otro dato que puede tener información sensible o que hagas más consultas de las necesarias.

      1. Me gustó esa parte de enviar datos adicionales por estar ligados. Imagino te refieres a los @Join. Gracias por aclarar mis dudas con respecto a los DTO.

  6. lo entiendo todo.
    pero tengo un problema
    tengo 3 entidades
    Alumno, matricula, pagos
    en la repositorio List findByDni(Long dni);
    me trae los datos 3 tablas
    pero yo quiero mostrar los pagos ingresando el dni del alumno.
    podrias pasar el codigo del proyecto completo para analizar dao y los servicios

  7. Impecable explicación. Y además, suelo utilizar una clase converter, que se encarga de pasar de Entidad a DTO y viceversa, para facilitar ese “traspaso” de datos. ¿Utilizás algo parecido, o simplemente creas el dto y seteas sus valores en el mismo método en el que se consultó a base de datos? Muchas gracias, saludos.

      1. Hola, y me refería a ese justamente, pero creo que no me expliqué bien. Me refiero a la forma de pasar los datos de la clase java Entidad, a la clase java DTO.

        //Create dto
        CustomerDTO dto = new CustomerDTO();
        dto.setAddress(address.getAddress());
        dto.setCountry(address.getCountry());
        dto.setZipCode(address.getZipCode());
        dto.setFullName(customer.getFirstname() + ” ” + customer.getLastname());
        dto.setId(customer.getId());

        Lo hacés siempre en el método en el que se requiere? o tenés algo así como una función con parámetros, para poder hacer esa “conversión” en cualquier parte del sistema.
        Saludos

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *