El enunciado pedía una API en Spring Boot 2 / Java 11 para mantener una lista de súper héroes: consultarlos todos, consultarlos por id, buscarlos por un fragmento de su nombre, modificarlos y borrarlos, con persistencia en una H2 en memoria y al menos un test unitario. Además había una lista de puntos opcionales (DDL con librería, anotación de tiempos, gestión de excepciones, tests de integración, Docker, caché, documentación, seguridad) que sabía que no me daría tiempo a cubrir todos, así que decidí desde el principio priorizar lo obligatorio y dejar sentada una base de arquitectura limpia sobre la que esos opcionales pudieran añadirse después sin fricción.
El proceso, commit a commit
Arranqué el repositorio en local. No hacía falta publicarlo, el enunciado permitía entregarlo comprimido, pero preferí llevarlo por Git desde el minuto uno para que el historial de commits sirviera como evidencia del proceso, tal y como sugería el punto de "se valorará positivamente el uso de TDD... se pueden utilizar los commits para ver el proceso".
Generé el esqueleto del proyecto y elegí las dependencias pensando ya en varios de los puntos opcionales:
- Spring Web + Data JPA + Data JDBC + Data REST para la capa de API y persistencia.
- H2 en memoria, tal y como pedía el enunciado.
- Liquibase para no escribir el DDL a mano — cubre el opcional de "librería que facilite el mantenimiento de los scripts DDL".
- MapStruct para no escribir a mano el mapeo entre entidades JPA y los objetos que viajan por la API.
- spring-restdocs-mockmvc pensando en generar documentación de la API a partir de los tests, aunque al final el tiempo no me llegó para completar esa parte.
Este fue el commit grueso: monté la arquitectura en capas y los tres primeros endpoints de consulta.
SuperheroeEntity+ changelog de Liquibase (superheroes.sql) que crea la tabla y siembra 10 héroes de ejemplo.- Separación
Entity(persistencia) /DAO(entrada) /DTO(salida), conSuperheroeMapperde MapStruct entre medias, para no acoplar lo que guardo en base de datos con lo que expongo por la API. SuperheroesRepositorysobreJpaRepository, con un método derivadofindSuperheroeEntitiesByNameContainingIgnoreCasepara la búsqueda por nombre parcial que pedía el enunciado (el ejemplo de "man" → Spiderman/Superman/Manolito).GenericResponsecomo envoltorio único de respuesta (status, message, data) para que todos los endpoints respondan de forma consistente.URLConstantpara centralizar las rutas y no repetir strings mágicos por los controladores.- Los tres endpoints GET: todos, por id y por nombre.
Añadí el PUT para modificar un héroe existente, con validación básica de que el id exista antes de tocar nada.
Añadí el DELETE, completando así los cinco requisitos funcionales del enunciado.
Cerré la entrega con los tests unitarios de SuperheroeServiceImpl usando JUnit 5 y Mockito,
cubriendo el camino feliz y el de error (200/201 vs 404) de cada método del servicio. El enunciado solo
pedía "test unitarios de algún servicio", así que me centré en cubrir bien ese único servicio en vez de
repartir esfuerzo en tests superficiales de más componentes.
Decisiones de diseño
Respuesta uniforme
GenericResponse envuelve status, mensaje y datos en todas las respuestas, para que el frontend consumidor tenga un contrato predecible sin tener que interpretar el código HTTP a ciegas.
Capas desacopladas
Entity ↔ DAO/DTO vía MapStruct, controlador delgado que delega en el servicio, y el servicio hablando solo con el repositorio. Cada pieza tiene una única razón para cambiar.
DDL versionado
Liquibase gestiona el esquema y la carga inicial de los 10 héroes de ejemplo, en vez de un schema.sql/data.sql sueltos.
Rutas centralizadas
URLConstant agrupa los fragmentos de ruta para que no queden strings repetidos y desincronizados entre controlador y tests.
Qué quedó cubierto y qué no
Siendo honesto con el resultado final, de los puntos opcionales del enunciado:
- ✓ Librería para el mantenimiento del DDL — Liquibase.
- · Anotación personalizada para medir tiempos de ejecución (estilo
@Timed) — no llegué a implementarla. - · Gestión centralizada de excepciones — los controladores validan a mano, sin
@ControllerAdvice. - · Test de integración — solo hay test unitario del servicio.
- · Aplicación dockerizada — no incluí Dockerfile.
- · Caché de peticiones — no implementada.
- · Documentación de la API — añadí la dependencia de Spring REST Docs pero no generé la documentación final.
- · Seguridad del API — no añadida (el
CrossOrigin("*")queda abierto).
El detalle de cómo cerrar cada uno de estos puntos, junto con otras mejoras que veo con perspectiva de los años, está en el documento de mejoras pendientes.