Todo empezó con un enunciado tras el proceso de selección de Inditex: una prueba técnica pequeña para ver cómo diseño y construyo un servicio. Nada de sistemas grandes ni de mucha funcionalidad — solo un endpoint de consulta sobre una tabla de precios, y una advertencia clara sobre qué se iba a valorar: diseño y construcción del servicio, calidad de código y resultados correctos en los tests.
El reto
El enunciado pedía tres cosas muy concretas:
- Un endpoint REST que, dados una fecha de aplicación, un producto y una cadena, devuelva la tarifa de precios que corresponde aplicar.
- Una base de datos en memoria (H2) inicializada con los cuatro registros de ejemplo del enunciado.
- Tests que validasen cinco peticiones concretas contra esos datos: distintas horas de los días 14, 15 y 16 de junio de 2020 para el mismo producto y la misma cadena.
Con eso en mente, y sabiendo que el foco estaba en la calidad del código más que en la cantidad de funcionalidad, me puse manos a la obra.
Construir de dentro hacia afuera
No arranqué por el controlador. Empecé modelando el dominio: un record
Price que refleja la tabla, y dos DTOs, PriceQueryRequest y
PriceQueryResponse, con la entrada y la salida exactas que pedía el
enunciado. Solo después construí el acceso a datos, el servicio y por último
el controlador. Empezar por el dominio evita diseñar el modelo en función de
cómo llega la petición HTTP.
Para el acceso a datos elegí JdbcTemplate en vez de JPA/Hibernate: es
una única consulta sobre una única tabla, sin relaciones entre entidades, así
que meter un ORM completo habría sido más peso del que el problema pedía. El
repositorio quedó reducido a una consulta que filtra por marca, producto y
rango de fechas, ordenada por prioridad.
Encima de eso, un PriceService que aplica la regla de negocio y
construye la respuesta, y un PriceController con un único
GET /prices que solo recibe parámetros y delega.
La decisión que más pesé: dónde vive la prioridad
El enunciado deja clara la regla: si dos tarifas coinciden en la misma fecha,
gana la de mayor prioridad. Podría haberme apoyado en el propio
ORDER BY PRIORITY DESC de la consulta y quedarme con la primera
fila que llegara. Funciona, pero esconde una regla de negocio importante
dentro de una query SQL, donde nadie la ve a simple vista y nadie la testea
de forma aislada.
Preferí resolverlo de forma explícita en el servicio, comparando la prioridad
de los candidatos que devuelve el repositorio. Es un poco más de código, pero
deja la regla donde se puede leer, tocar y testear sin depender de cómo esté
escrita la query. Apliqué el mismo criterio al caso de "no hay ninguna tarifa
aplicable": en vez de devolver null o una lista vacía, lo
convertí en una excepción propia, para que ese caso no se pueda pasar por
alto en ninguna capa superior.
Verificarlo con tests
El propio enunciado pedía tests para las cinco peticiones de ejemplo, y además se me pidió explícitamente que fueran solo tests unitarios, sin integración ni end-to-end. Así que cada capa se testeó a su propio nivel: el repositorio contra una base H2 real en memoria, porque ahí lo que quiero validar es que el SQL es correcto; el servicio y el controlador con sus dependencias simuladas (Mockito), porque su lógica no depende de si hay una base de datos o un servidor HTTP real detrás.
Cubrí el listado de una tarifa única, el solapamiento de varias tarifas con la selección por prioridad, el caso sin resultados y el de producto inexistente. Como comprobación final, aparte de la batería automática, levanté la aplicación real y lancé a mano los cinco casos del enunciado contra el endpoint: los cinco devolvieron exactamente la tarifa y el precio esperados.
Dejarlo documentado
Por último, volqué en el README.md el enunciado completo, el
stack elegido y por qué, cómo levantar la aplicación, cómo consultar el
endpoint y cómo ejecutar los tests, para que cualquiera pueda arrancar el
proyecto y verificar el resultado sin más contexto que ese fichero.
El resultado
Una API pequeña, con la regla de negocio en un sitio explícito y testeable, y sin más complejidad de la que el problema pedía — que, al final, era justo el objetivo de la prueba.