07 - Persistencia con JPA
El punto débil que dejó el hito 06 — stock reservado sin pedido cuando la persistencia falla — es el mejor argumento para este hito. Aplica el tema 05 — Persistencia: Spring JPA: la inversión de dependencias ya existe desde la partida — las interfaces de repositorio viven en domain —, así que lo que cambia es el mecanismo detrás de ellas: la persistencia JDBC se sustituye por JPA/Hibernate, repositorios de Spring Data y mapeos con MapStruct, sin tocar dominio, casos de uso, contrato ni API pública. La paginación a mano con JDBC también desaparece: Spring Data la resuelve.
| Pieza | Cambio en este hito |
| Persistencia | DAO JDBC → entidades JPA y repositorios Spring Data, en cada módulo |
| Dominio | Sin cambios: las interfaces de repositorio siguen definidas en domain |
| Paginación | LIMIT/OFFSET a mano → Pageable/Page de Spring Data |
| Mapeos | MapStruct entre entidades y modelos del dominio |
| Transacciones | Crear y cancelar pedidos se vuelven atómicos |
| Pruebas | Testcontainers: MariaDB real en las pruebas de persistencia |
1. El nuevo mecanismo de persistencia
Dependencia Data JPA de Spring (el driver de MariaDB ya entró con la partida). El esquema lo sigue gestionando el init.sql: las entidades mapean contra él y no lo generan.
Entidades
JPA para las tablas de cada módulo, con sus relaciones — el producto con su catálogo, el pedido con sus líneas — según las decisiones que muestra el
tema 05.
Repositorios de
Spring Data junto a las entidades: las consultas se derivan de la firma de sus métodos. La paginación del catálogo pasa a lo que ya trae — el
Pageable entra y la
Page devuelve el contenido y los metadatos que el contrato declara —: el recuento a mano del
hito 04 desaparece.
Las implementaciones de las interfaces del dominio se reconstruyen sobre las piezas nuevas, y los mapeos entre entidades y modelos del dominio se declaran con MapStruct del modo que muestra el tema. El dominio sigue sin conocer JPA, MapStruct ni Spring: se comprueba en los imports de sus clases.
2. Transacciones
La transacción cruza módulos porque escriben en la misma base de datos. Es el piso del tema 08: una operación auxiliar — enviar la factura, avisar de stock bajo — no debería entrar en esa transacción; de ahí los eventos.
3. Pruebas de persistencia
Criterio del nivel: las pruebas del repositorio van contra la base de datos real del proyecto, no contra un sustituto. La decisión de la práctica: Testcontainers con la misma imagen de MariaDB del curso (fijada en la especificación).
Catálogo: guardar y recuperar mantiene el producto intacto — catálogo, precio y stock como corresponden; listar; borrar.
Paginación: el listado de un catálogo respeta tamaño, orden y totales, con los metadatos que Spring Data aporta.
Pedidos: guardar cabecera y líneas; recuperar el pedido con sus líneas y sus precios capturados.
Transacción: crear un pedido cuya segunda línea no tiene stock deja el catálogo sin cambios y sin pedido — la prueba integra ambos módulos sobre el contenedor.
Las pruebas anteriores no cambian: dominio sin base de datos, presentación con dobles, límites con sus verificaciones.
Comprobación
Ya no queda JDBC: todo acceso a datos pasa por las interfaces de repositorio del dominio, implementadas en persistence con JPA.
El listado paginado responde igual que en el hito 04, pero sin SQL de paginación escrito a mano.
El dominio no importa JPA, MapStruct ni Spring (comprobar con los imports y con las reglas ArchUnit).
Las pruebas de persistencia corren contra MariaDB en Testcontainers.
Un pedido con una línea imposible no deja nada a medias: transacción probada.
El contrato no cambia (sin subida de versión); commit y tag hito-07.
Siguiente hito: hito 08 — Login con JWT.