DTO’s: ¿Que son y por que se utilizan?
Buscando información para escribir este blog post encontré una muy buena respuesta para que son DTO’s en stackoverflow.
Parafraseando lo que decía la contestación:
Un Data Transfer Object es un objeto que encapsula data (guarda data de una forma específica) y que se utiliza para enviar esa data de un subsistema hacia otro
Así que supongamos que tenemos tres capas en una aplicación. Un servicio que interactúa con la base de datos, un Api que llama al servicio y devuelve la respuesta y una interfaz grafica que presenta la información.
El servicio hace queries contra la base de datos y nos trae información de acuerdo a un modelo (una clase con propiedades o un record), tal vez nuestro modelo tiene alguna propiedad que no queremos hacer pública ya que la información es sensitiva como un id o un número de seguro social. Así que cuando el Api recibe los resultados antes de enviarlos de vuelta al cliente (el GUI) podemos utilizar un DTO para devolver solamente las propiedades necesarias.
Ejemplo en una aplicación simple
En esta aplicación tendremos un servicio con algunos modelos que son clases con propiedades basadas en una base de datos relacional. Estos models podrían contener información sensitiva como el ejemplo que les di anteriormente.
public class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public long SocialSecurityNumber { get; set; } //Obviamente esto nunca se guardaria asi pero lo hice por ejemplo
}
Nuestro servicio va a devolver una lista de personas o una persona especifica, cuando devuelva esta información va a contener la propiedad sensitiva que no queremos exponer, está siendo el número de seguro social.
Como no quería conectar este proyecto a una base de datos real lo que hice fue que en el constructor de esta clase le añadí información a una lista que estaré utilizando en los ejemplos.
public class PeopleInformationService
{
private List<Person> peopleList = new List<Person>();
public PeopleInformationService() => peopleList.Add(new Person
{
Id = Guid.NewGuid(),
LastName = "Rivera",
Name = "Genesis",
SocialSecurityNumber = 444-444-444
});
public async Task<List<Person>> GetPeopleList()
{
return peopleList;
}
public async Task<Person> GetPerson()
{
return peopleList.First();
}
}
Ahora nuestro Api cuando llama al servicio y utiliza la información, antes de devolverla al cliente hacemos “Mapping” y modelamos la información de acuerdo a un DTO que creamos.
Nuestro DTO:
public class PersonDTO
{
public string Name { get; set; }
public string LastName { get; set; }
}
Ahora podemos ver en el controller de nuestro Api que inyectamos el PeopleInformationService y utilizamos los métodos que habíamos creado en el. Sin embargo devolvemos los resultados de acuerdo a las propiedades de nuestro DTO.
[Route("api/[controller]")]
[ApiController]
public class PeopleController : ControllerBase
{
private readonly PeopleInformationService peopleService;
public PeopleController(PeopleInformationService service)
{
peopleService = service;
}
[Route("getperson")]
public async Task<PersonDTO> GetPerson()
{
var person = await peopleService.GetPerson();
return new PersonDTO
{
LastName = person.LastName,
Name = person.Name
};
}
[Route("getpeople")]
public async Task<List<PersonDTO>> GetPeople()
{
var personList = await peopleService.GetPeopleList();
var mappedPersonList = new List<PersonDTO>();
foreach(var person in personList)
{
mappedPersonList.Add(new PersonDTO
{
LastName = person.LastName,
Name = person.Name
});
}
return mappedPersonList;
}
}
Ahora si utilizamos los endpoints creados en nuestro Api podemos observar que nos devuelve el objeto con los campos que definimos en nuestro DTO.
En Conclusión:
- Los DTOs nos ayudan grandemente con la seguridad de nuestra aplicación ya que solamente devolvemos la información necesaria y no exponemos campos sensitivos.
- El rendimiento de nuestra aplicación podría ser mejorado ya que solamente utilizamos los campos necesarios y no toda la data de las tablas de nuestra base de datos.