← Peticiones, respuestas y CRUD en memoria

Sesión 8 · Semana 4

Contrato en memoria listo para evolucionar

Proyecto compartido. En el taller de Intermodular que abre esta semana has trabajado enlaces rotos y formato. En Servidor continúas la implementación del mismo producto.

Se explica

25 minutos · explicación y demostración

Llegas con un CRUD en memoria y una colección que comprueba su contrato. El contrato es lo que el cliente puede esperar de cada petición. Hoy revisarás las entidades de tu proyecto con los mismos criterios y corregirás diferencias antes de cambiar su diseño en la UD3.

La diferencia que importa

Tu API funciona. Eso ya no es noticia: funciona desde la UD1.

Lo que se pide hoy es otra cosa, y es la que separa un ejercicio de un entregable:

Que otra persona pueda comprobar que funciona sin que tú estés delante.

Piensa en qué haría un compañero que abriese tu repositorio ahora mismo. ¿Sabría arrancarlo? ¿Sabría qué endpoints hay? ¿Sabría qué debe responder cada uno? ¿Podría comprobarlo sin escribir una sola petición a mano?

Si la respuesta a las cuatro no es sí, el trabajo no está terminado aunque el código sea correcto.

Verificar un contrato sin mirar la implementación

Un contrato describe qué puede pedir el cliente y qué resultado debe observar. Incluye métodos, rutas, campos y estados; también los casos de error. Puede cumplirse con una lista en memoria o con una base de datos. Precisamente por eso vamos a fijarlo antes de sustituir el almacenamiento.

La comprobación comienza con datos que prepara la propia colección. Crear un recurso y guardar el identificador de su respuesta evita depender de que alguien haya dejado previamente el registro 1. Al final se limpian los datos creados o se utiliza un conjunto de prueba identificado. Una segunda ejecución debe producir resultados equivalentes.

Si falla una petición, se distingue el contrato de su implementación: una ruta mal escrita es un problema de la prueba; una ruta acordada que devuelve una respuesta incorrecta es un defecto del servidor. Se conserva la petición que demuestra el problema y se corrige la pieza correspondiente. Revisar esta versión significa poder explicar ambos lados de esa comparación.

Se trabaja

140 minutos · implementación guiada sobre el proyecto propio

Paso 1 · Retomar el proyecto y preparar la comprobación

  1. Abre la colección de la sesión 7 y ejecútala contra tu aplicación recién arrancada. Guarda qué comprobaciones pasan y cuáles fallan.
  2. Abre el README y los controladores de todas tus entidades. Contrasta las rutas documentadas con las que existen en el código.
  3. Prepara una tabla con requisito, petición que lo comprueba y resultado. Usa el dominio que elegiste, no una nueva aplicación de ejemplo.

Paso 2 · Revisar la versión en memoria: ejemplo de criterios de aceptación

Trabaja sobre las entidades existentes. Primero comprueba el modelo contra la tabla y añade únicamente los campos que falten. Si falta proyectoId en Tarea, declara el atributo y sus accesos, permite enviarlo al crear y devuelve su valor al consultar; de momento es una referencia numérica, no una relación JPA. Después recorre la tabla de operaciones, asociando cada fila a un método y a una petición guardada. Corrige una fila y repítela antes de seguir.

Modelo

Clase Campos mínimos
Proyecto id, nombre, descripcion, activo
Tarea id, titulo, prioridad, completada, proyectoId

Los identificadores los asigna el servidor y no se repiten aunque se borren elementos.

Endpoints exigidos

Método y ruta Caso Respuesta
GET /proyectos Siempre 200 con el array
GET /proyectos?activo=true Filtro opcional 200 con los que coincidan
GET /proyectos/{id} Existe / no existe 200 / 404
POST /proyectos Correcto 201, cuerpo y Location
PUT /proyectos/{id} Existe / no existe 200 / 404
PATCH /proyectos/{id} Existe / no existe 200 / 404
DELETE /proyectos/{id} 204
GET /tareas Siempre 200 con el array
GET /tareas?completada=true Filtro opcional 200 con las que coincidan
GET /tareas/{id} Existe / no existe 200 / 404
POST /tareas Correcto 201, cuerpo y Location
PUT /tareas/{id} Existe / no existe 200 / 404
PATCH /tareas/{id} Existe / no existe 200 / 404
DELETE /tareas/{id} 204
GET /proyectos/{id}/tareas Proyecto existe 200 con sus tareas
GET /proyectos/{id}/tareas Proyecto no existe 404

Las dos últimas filas son las únicas que no has hecho nunca. Piensa antes de escribir: ¿en qué controlador vive esa ruta? ¿Qué distingue «este proyecto no tiene tareas» de «este proyecto no existe», y qué debe responder cada caso?

Reglas que se comprueban

  1. Ninguna ruta contiene un verbo. La acción la expresa el método HTTP.
  2. Ninguna operación de escritura acepta el id del cuerpo: manda la ruta.
  3. PUT sustituye por completo; PATCH solo toca lo que recibe.
  4. Una colección sin resultados devuelve 200 con [], nunca 404.
  5. El filtro es opcional; sin él salen todos los elementos.
Estoy atascado · la ruta anidada

Fíjate en qué identifica y qué filtra. El {id} del proyecto identifica, así que va en la ruta; eso ya lo decidiste en la UD1.

Para saber qué tareas son suyas necesitas recorrer la lista de tareas comparando su proyectoId. Antes de eso debe comprobarse que el proyecto existe: si no existe, la respuesta no es una lista vacía.

Paso 3 · La prueba de aceptación

Prepara un escenario que cree sus propios datos: alta de proyecto → guardar id del proyecto → alta de tarea con ese id → consultas y modificaciones → borrar tarea → borrar proyecto. Guarda los ids en variables distintas. Ejecuta la secuencia dos veces sin reiniciar para detectar si depende de datos residuales; después reinicia y ejecútala otra vez para comprobar que sabe prepararse desde memoria vacía.

El quinto criterio es el que separa una colección de una lista de peticiones. Si la segunda ejecución falla, es que el escenario deja datos, o que da por hecho un estado inicial que ya no se cumple.

Paso 4 · Preparar la evidencia de esta versión

En tu repositorio del módulo:

  1. El proyecto completo, arrancable con mvnw spring-boot:run.
  2. La colección exportada, en una carpeta pruebas/.
  3. Un README.md que quepa en una pantalla y responda a tres cosas: cómo se arranca, qué endpoints hay y cómo se ejecuta la colección.
  4. Un las decisiones técnicas con estas cuatro, cada una en dos o tres frases:
    • Qué responde tu DELETE sobre algo inexistente, y por qué elegiste eso.
    • Qué hace tu API si el id del cuerpo no coincide con el de la ruta.
    • Qué devuelve GET /proyectos/{id}/tareas cuando el proyecto existe y no tiene tareas, y por qué no es un 404.
    • Si dejaste Jackson tolerante o estricto con las claves desconocidas, y qué pierdes con tu elección.

El README no es burocracia

Es la parte del entregable que se lee primero y la que decide si alguien puede usar tu trabajo. Un backend excelente con un README que no explica cómo arrancarlo es, para quien llega nuevo, un backend que no funciona.

Paso 5 · Comprobar el resultado

Pásate esta lista tú mismo. Es la misma con la que se corrige.

Comprobación Cómo lo verificas
Arranca desde cero Clona tu propio repositorio en otra carpeta y arráncalo
Los endpoints están completos La colección cubre las dieciséis filas
Los códigos son correctos La colección está en verde
Es repetible La ejecutas dos veces seguidas
Se entiende sin ti Se lo das a un compañero y no te pregunta nada
Las decisiones están escritas las decisiones técnicas responde a las cuatro

La quinta es la de verdad. Dáselo a alguien y no le expliques nada. Cada pregunta que te haga es una línea que le falta a tu README.

Paso 6 · Lo que esta versión todavía hace mal

Compruébalo y anótalo, porque es el índice de la UD3:

Prueba esto Lo que pasa Lo correcto Dónde se arregla
POST /tareas con {} Crea una tarea sin nada 400 diciendo qué falta UD3
POST con prioridad "urgentísima" La acepta 400: no es un valor válido UD3
POST /tareas con un proyectoId inexistente La acepta 400 o 404, pero no un 201 UD3
Cualquier 400 Cuerpo genérico e inútil Un mensaje que diga qué corregir UD3
Añadir un campo interno al modelo Se publica solo Se publica lo que tú decidas UD3, con DTO
Reiniciar Se pierde todo Sigue ahí UD5

Fíjate en la tercera fila: tu API acepta tareas que pertenecen a proyectos que no existen. Nada en el código lo impide, porque nadie ha escrito todavía qué es una tarea válida.

Objetivo mínimoLas dieciséis filas implementadas con sus códigos correctos y la colección cubriéndolas.
Si lo tienesLa colección ejecutable dos veces seguidas, con README y DECISIONES escritos.
RetoUn compañero clona tu repositorio, lo arranca y ejecuta la colección sin preguntarte nada.
Ver respuestas

1 · Que otra persona pueda arrancarlo y comprobar que funciona sin que tú estés delante: código, pruebas ejecutables y documentación mínima.

2 · Porque deja el servidor en un estado distinto del que suponía, así que a partir de la segunda vez sus comprobaciones dejan de significar nada.

3 · Un 200 con un array vacío. El recurso existe; lo que no hay son elementos, y eso no es un error.

4 · Porque no hay validación: nadie ha escrito todavía qué condiciones debe cumplir una tarea para ser válida, así que Jackson construye el objeto y el controlador lo guarda.

Paso 7 · Comprobar y registrar el resultado del proyecto

  1. Reproduce el ciclo CRUD de la entidad principal y de las relacionadas previstas hasta ahora. Cada operación debe coincidir con su contrato documentado.
  2. Reinicia, recrea los datos mediante la colección y ejecútala de nuevo. Deja registrado que perder datos al reiniciar sigue siendo una limitación conocida de esta versión.

Cierre

15 minutos · resultado comprobable y explicación individual

Al terminar la sesión:

Otra persona reproduce las operaciones principales y un fallo previsto usando únicamente el repositorio y sus instrucciones.

Cada integrante explica una decisión del código apoyándose en una de las comprobaciones realizadas.