Separación de dominio y persistencia con DTOs en Java
Clase 11 de 23 • Curso de Java Spring Boot
Resumen
Una API robusta, limpia y preparada para cambios necesita una arquitectura sólida. ¿Por qué? Porque un código acoplado a la base de datos limita la evolución y expone detalles que deberían permanecer internos. Aquí exploramos cómo estructurar adecuadamente una API de películas en Java usando buenas prácticas, enfocándonos en la introducción de la capa de dominio y el uso del patrón Data Mapper.
¿Cómo afecta el acoplamiento entre controlador y base de datos el diseño de tu API?
Actualmente, el controlador llama directamente al CRUD del Movie Entity, exponiendo la estructura interna de la base de datos. Esto implica varios riesgos:
- Alto acoplamiento: cualquier cambio en la base de datos puede romper la API fácilmente.
- Exposición de datos sensibles o innecesarios: al no filtrar la salida, pueden aparecer campos indeseados.
- Mezcla de idiomas: el endpoint es /movies pero el contenido de la respuesta está en español.
Estos problemas hacen que la API sea difícil de mantener, frágil y dependiente de la estructura interna de la base de datos.
¿Por qué es clave separar responsabilidades entre capas en una API?
Separar responsabilidades permite que cada parte del sistema cumpla una función clara y focalizada. Dejar fuera la capa de dominio va contra los principios de una buena arquitectura. Con una correcta separación:
- Cada capa conoce solo lo necesario de las demás.
- La evolución de una capa no obliga a reescribir las otras.
- La lógica de dominio es independiente de la infrastructura técnica.
El patrón Data Mapper ayuda a lograr esto separando el modelo de dominio de cómo se expone en la API o se almacena en la base de datos.
¿Cómo implementar Movie DTO y Movie Repository siguiendo buenas prácticas?
- Definir el Movie DTO (Data Transfer Object):
- Se crea dentro del paquete
domain.dto
. - Usando Java record se obtiene una clase inmutable adecuada para representar los datos expuestos.
-
Ejemplo de atributos:
- String title (en inglés, consistente con la API)
- Integer duration
- String genre
- LocalDate releaseDate
- Double rating
-
Crear la interfaz Movie Repository:
- Ubicada dentro del paquete
domain.repository
. - Define el método
getAll
, que retorna una lista de MovieDTO. -
Permite desacoplar la lógica del acceso a la base de datos.
-
Implementar Movie Service:
- Ubicada en el paquete
service
. - Se anota con @Service para participación en el ecosistema Spring.
- Recibe por inyección la interfaz MovieRepository.
-
Expone el método
getAll
, que internamente usa la interfaz y retorna los MovieDTO. -
Actualizar el Movie Controller:
- Ahora inyecta MovieService en vez del CRUD directamente.
- Expone el endpoint de películas solicitando la lista a MovieService, ya desacoplada del Entity o CRUD.
¿Qué ventajas aporta esta nueva estructura a tu API?
- Reduce la dependencia directa de la base de datos.
- Los cambios internos (estructura, nombres o tipo de base) no afectan el contrato público de la API.
- Facilita la evolución y el mantenimiento.
- Permite cumplir con convenciones internacionales en endpoints y respuestas.
¿Qué sigue para perfeccionar la capa de persistencia?
Aún falta adaptar la capa de persistencia para que devuelva MovieDTO en vez de MovieEntity. Además, se mencionó el uso de map struct para transformar entre entidades y DTOs y así seguir manteniendo un dominio limpio y desacoplado.
¿Te gustaría saber cómo implementar la conversión entre entidades y DTOs usando map struct? ¡Comparte tus dudas o comenta tu experiencia en proyectos similares!