05 - Validación e invariancias

Con el CRUD de catálogos y productos del hito 04, la API ya existe y es exactamente la del contrato; lo que falta es rechazar correctamente lo que no admite. Este hito aplica el tema 03 — Validación e invariancias: dos preguntas distintas, respondidas en lugares distintos.

Pregunta Dónde se responde Mecanismo Respuesta
¿Cumple la petición el formato del contrato? La petición de entrada, en la capa de presentación Jakarta Validation 400 con campo y motivo
¿El estado propuesto es válido para el negocio? El dominio Invariantes en los modelos Excepción de dominio, mapeada al error del contrato
  1. Ampliar openapi.yaml (v0.1.1) con lo que la API admite: los constraints se redactan en el propio documento, porque describen el contrato, no el código.
Esquema Constraint
CatalogRequest.name minLength 1 · maxLength 80
CatalogRequest.description maxLength 240
ProductRequest.name minLength 1 · maxLength 80
ProductRequest.description maxLength 240
ProductRequest.price minimum 0.01 · maximum 999999.99
ProductRequest.stock minimum 0 · maximum 100000
ProductRequest.catalogId minimum 1
  1. Regenerar, con el generador preparado para ello (opciones en el tema 02): los constraints llegan al esqueleto como anotaciones Jakarta Validation, y la validación se dispara en el punto declarado por el propio contrato.
  • Las respuestas de error de la API salen en formato ProblemDetail (RFC 9457): el esquema Problem que el contrato declara, materializado.
  • A la base que trae Spring WebMVC se añade el manejo global de excepciones de la presentación, como expone el tema 03: el 400 con el detalle — campo y motivo por cada violación — y las excepciones del dominio mapeadas a sus estados (404 para lo inexistente). Las excepciones propias del dominio del catálogo viven en él; el formato lo da la presentación.

La petición válida según el contrato puede seguir proponiendo un estado imposible para el negocio: el precio y el stock deben protegerse en el dominio.

  1. Crear los value objects del catálogo en domain, según la especificación: Money (precio: decimal mayor que cero, escala de 2) y Stock (entero no negativo). Cada uno valida en su construcción y lanza la excepción de dominio propia.
  2. El modelo Product pasa a componerse con ellos; la creación y la modificación de productos construyen primero los value objects: si una invariante falla, el estado inválido nunca llega a la persistencia.
  3. El mismo dato puede aparecer en las dos fronteras: minimum en el contrato (formato) y la validación del Money (invariante). No es redundancia: protegen fronteras distintas, como explica el tema.
La invariante no se confía a la validación de la petición: el value object la garantiza para cualquier código que construya un Money, no solo para el flujo que llega validado.

Criterios de prueba de este nivel, según el tema: qué probar y qué aislar.

  • Value objects (unitarias): Money y Stock construidos dentro de rango y rechazados fuera; sin Spring, sin base de datos.
  • Servicio (unitarias con un doble del repositorio): la creación y la actualización construyen el modelo a través de los value objects; un producto con catalogId desconocido no se llega a crear, y un producto inexistente al modificar o borrar falla con la excepción de dominio correspondiente.
  • Presentación (con el servicio doblado): una petición que no cumple cada constraint — de catálogo o de producto — responde 400 con las violaciones; un recurso inexistente — sintácticamente válido — responde 404; una correcta, 200 / 201 / 204.
  • Una petición con precio 0 o stock negativo responde 400 antes de tocar el dominio: es la constraint del contrato.
  • Un catálogo con nombre vacío responde 400 por la misma vía.
  • El mismo rango está garantizado por los value objects en pruebas unitarias: construir un Money con precio 0 lanza la excepción de dominio.
  • Un catálogo o producto inexistente en ficha, modificación o borrado responde 404 con el problema coherente.
  • El error 400 muestra campo y motivo en el formato Problem del contrato.
  • Versión del contrato 0.1.1; commit y tag hito-05.

Siguiente hito: hito 06 — Módulos: pedidos y API pública.

  • clase/daw/dws/1eval/practicas/p5.txt
  • Última modificación: 2026/10/28 13:32
  • por cesguiro