El método
La secuencia crece otra vez. Estos son los pasos de la UD1 y la UD2 con los tres que añade el diseño:
- ¿De qué cosa hablo? Un sustantivo, en plural si es colección. Nunca un verbo
- ¿Qué le hago? Eso elige el método HTTP, y con él si es idempotente
- ¿Qué datos necesito? Ruta si identifican, query si filtran, cuerpo si son contenido
- ¿Qué acepto y qué publico? Dos listas distintas, dos clases distintas
- ¿Qué tiene que cumplir lo que llega? Y qué digo cuando no lo cumple
- ¿Cómo puede terminar esto? Cada final, su código de estado
- ¿Cómo demuestro que sigue funcionando mañana?
El paso uno decide más que ningún otro: si eliges bien el sustantivo, los cinco métodos HTTP te dan la mitad de la API sin pensar. Si eliges un verbo, cada funcionalidad nueva te obligará a inventar una ruta.
La idea más importante
Tu API es una promesa, y las promesas se hacen a alguien que no está delante. Todo lo que obligue a preguntarte algo es un defecto de diseño, aunque funcione perfectamente.
De ahí sale la unidad entera. Por eso las rutas se nombran para que se puedan adivinar, por eso el modelo no se publica, por eso un 400 dice qué campo corregir, y por eso todos los errores tienen la misma forma: para que nadie tenga que escribirte.
Lo que cambia por dentro y lo que se promete fuera son dos cosas
Modelo, DTO y mapper existen por esa frase. El modelo cambia cuando cambia tu código; el contrato cambia cuando decides romper una promesa. Si son la misma clase, cualquier refactorización es una promesa rota sin querer.
Las decisiones que tienes que saber justificar
| Decisión | Lo que tienes que poder decir |
|---|---|
| La ruta lleva sustantivos | La acción ya la expresa el método; con verbos, la API deja de ser adivinable |
| Ruta anidada o filtro | Anidada si el recurso pertenece a otro; filtro si acota una colección propia |
| Una acción que no es CRUD | Primero cambio de estado, luego recurso nuevo, y solo entonces acción explícita |
| Tres clases por recurso | Lo que acepto, lo que manejo y lo que publico tienen motivos de cambio distintos |
| El DTO de entrada como lista blanca | Lo que no existe en la clase no se puede asignar: previene el mass assignment |
| Tipos envoltorio en los DTO | Un primitivo no distingue «ausente» de su valor por defecto |
| Un solo mapper | Con dos sitios que traducen, el mismo recurso acaba publicándose de dos formas |
| Validar en el DTO, no en el modelo | El DTO es la frontera con lo que no es de fiar; el modelo lo construye tu código |
| Mensajes que dicen qué se espera | Un mensaje accionable se puede enseñar al usuario final sin traducirlo |
| Un único formato de error | Quien consume escribe el tratamiento una vez y le sirve para toda la API |
El 500 no cuenta nada |
El mensaje de una excepción puede filtrar rutas, consultas o datos ajenos |
| Nivel 2 y no hipermedia | Coste alto y beneficio bajo con los clientes reales; es una decisión, no un olvido |
Al terminar deberías poder responder
- ¿Por qué devolver JSON no convierte una API en REST?
- ¿Qué tres restricciones de REST cumples solo por usar HTTP bien?
- ¿Qué distingue el nivel 1 del nivel 2, y el 2 del 3?
- ¿Por qué una URL no debe contener un verbo?
- ¿Cuándo se usa ruta anidada y cuándo un filtro?
- Enumera las tres estrategias para exponer algo que no es CRUD, en orden.
- ¿Qué diferencia hay entre un recurso, un modelo y una representación?
- Nombra un campo que entra y no sale, y otro que sale y no entra.
- ¿Qué es el mass assignment y por qué un DTO de entrada lo previene?
- ¿Por qué un DTO de modificación usa
Booleany noboolean? - ¿Qué fallo concreto produce tener la conversión repartida en dos sitios?
- ¿Por qué el mapper conoce el modelo y el DTO no?
- ¿Qué comprueba una prueba de ida y vuelta que no comprueba un código de estado?
- ¿Por qué la validación del navegador no protege nada?
- Las anotaciones de validación no hacen nada. ¿Qué compruebas primero?
- Diferencia entre
@NotNull,@NotEmptyy@NotBlank, con un valor que las separe. - ¿Por qué un validador propio debe dejar pasar el nulo?
- ¿Por qué un
enumes mejor que validar un texto? - ¿Qué reglas no puede comprobar Bean Validation, y por qué?
- ¿Por qué el manejador genérico no devuelve el mensaje de la excepción?
- ¿Cuándo se responde
409en lugar de400o404? - ¿Qué tipo de fallo no detecta ninguna prueba de un endpoint aislado?
Si además puedes coger un dominio nuevo y escribir su contrato de recursos, sus DTO, sus reglas y su formato de error antes de programar nada, tienes lo que esta unidad quería darte.
El vocabulario de la unidad
| Concepto | Significa |
|---|---|
| REST | Estilo de arquitectura, no un protocolo ni una librería ni un formato |
| Interfaz uniforme | Que todos los recursos se identifiquen y manipulen con las mismas reglas |
| Niveles de madurez | El mapa de 0 a 3 que sitúa cualquier API |
| Hipermedia | El nivel 3: respuestas que incluyen los enlaces a lo que se puede hacer después |
| Recurso | Una cosa del dominio a la que se le puede dar una dirección |
| Colección | El conjunto de recursos de un tipo, en plural: /tareas |
| Ruta anidada | La que expresa pertenencia: /proyectos/7/tareas |
| Representación | El JSON concreto que viaja, con los campos que has decidido publicar |
| DTO | Clase cuyo único trabajo es transportar datos entre dos sitios |
| DTO de entrada | Lo que la API acepta. Funciona además como lista blanca |
| DTO de salida | Lo que la API publica |
| Mass assignment | Que el cliente asigne un campo que no debería poder tocar |
| Mapper | El único sitio del proyecto que traduce entre DTO y modelo |
| Prueba de ida y vuelta | Comprobar que un dato sobrevive al circuito completo de conversiones |
| Bean Validation | El estándar de restricciones declarativas: @NotBlank, @Size, @Pattern |
@Valid |
Lo que activa la comprobación de esas restricciones |
| Restricción propia | Anotación que expresa una regla del dominio, con su validador |
| Regla de negocio | La que necesita consultar datos para decidir. No es validación de formato |
@RestControllerAdvice |
La clase que atiende las excepciones de todos los controladores |
@ExceptionHandler |
El método que convierte un tipo de excepción en una respuesta |
409 Conflict |
Petición válida que choca con el estado actual de los datos |
| Problem Details | El estándar RFC 9457 para el cuerpo de un error, con su ProblemDetail en Spring |
Comprobación final del producto
Comprobación final · con el proyecto delante
- Ninguna ruta lleva verbos y todas las colecciones están en plural.
- Ningún
@RequestBodyrecibe una clase del modelo y ningún endpoint la devuelve. - Toda la traducción vive en el paquete
mapper. - Un cuerpo vacío responde
400nombrando los campos que faltan, en español. - Existe al menos una anotación de validación propia.
- Los seis tipos de error tienen exactamente la misma forma.
- Un
500no revela nada del interior y deja la traza en la consola. - La auditoría de la sesión 9 y la de la sesión 14 están las dos escritas.
Resultados de la unidad
- Distinguir una API HTTP cualquiera de una API orientada a recursos.
- Nombrar recursos y URLs sin meter verbos ni acciones en la ruta.
- Separar el modelo interno de lo que la API publica mediante DTO.
- Validar la entrada antes de que llegue a la lógica y explicar qué falla.
- Devolver errores con un formato único, predecible y útil para quien consume.
La siguiente unidad
Tres unidades, tres preguntas. Ya están las tres respondidas:
- UD1 · que responda
- UD2 · que responda correctamente
- UD3 · que esté bien diseñada
Existe una cuarta que no se ha abordado todavía:
¿Y esto quién lo mantiene?
Porque el contrato de tu API está bien, y detrás de él hay un controlador que guarda los datos, genera identificadores, busca, filtra y decide reglas de negocio. Todo junto, en una clase, sin una sola prueba que compruebe la lógica sin arrancar un servidor.
| Lo que sigue mal | Se arregla en |
|---|---|
| El controlador hace de todo | UD4, con capas |
| Una regla no se puede reutilizar en dos endpoints | UD4, con un service |
| Nada comprueba la lógica sin levantar la aplicación | UD4, con los primeros tests |
| «El proyecto debe existir» sigue sin tener sitio | UD4 |
| Al reiniciar se pierde todo | UD5, con PostgreSQL |
Fíjate en la cuarta fila. Esa regla la encontraste tú en la sesión 13, viste por qué una anotación de validación no podía resolverla y la dejaste apuntada. Lleva dos sesiones esperando un sitio donde vivir, y en la unidad siguiente lo encontrará.
El trabajo de estas tres semanas demuestra aquí su utilidad: la UD4 no cambiará ninguna ruta, ni un DTO, ni un código de estado. Va a reorganizar lo que hay detrás sin tocar el contrato. Tu colección de pruebas, que ya cubre la API entera, será exactamente lo que demuestre que no has roto nada por el camino.
Ya deberías ser capaz de
- Distinguir una API HTTP cualquiera de una API orientada a recursos.
- Nombrar recursos y URLs sin meter verbos ni acciones en la ruta.
- Separar el modelo interno de lo que la API publica mediante DTO.
- Validar la entrada antes de que llegue a la lógica y explicar qué falla.
- Devolver errores con un formato único, predecible y útil para quien consume.