====== 06 - Módulos: pedidos y API pública ====== Hasta aquí la tienda era un solo bloque por capas. Este hito aplica el [[temario:04-modulos-modulith|tema 04 — Monolito modular]]: se añade el segundo problema de negocio, **pedidos**, y con él la organización en módulos — cada uno protege su interior y expone una **API pública**. Los pedidos reservan el stock del catálogo por esa API: la primera colaboración síncrona entre módulos de la práctica. ^ Pieza ^ Cambio en este hito ^ | Contrato | v0.2.0: las operaciones de pedidos | | Estructura | Paquetes ''catalog'' y ''orders'' como módulos; **API pública** del catálogo | | Verificación | Límites entre módulos con **Spring Modulith**; capas internas con **ArchUnit** | ===== 1. El contrato se amplía ===== - Añadir a ''openapi.yaml'' (v0.2.0) las operaciones de pedidos y regenerar: ^ Operación ^ Petición ^ Respuestas ^ | Crear pedido | ''POST /api/orders'' · ''OrderRequest'' | 201 · 400 · 404 · 409 | | Ficha de pedido | ''GET /api/orders/{id}'' | 200 · 404 | | Cancelar | ''POST /api/orders/{id}/cancel'' | 200 · 404 · 409 | - Esquemas nuevos: ''OrderRequest'' (''userId'' y líneas), ''OrderLineRequest'' (''productId'', ''quantity''), ''OrderLine'' (''productId'', ''quantity'', ''unitPrice'') y ''Order'' (''id'', ''userId'', ''state'', ''lines'', ''total''). - El **409** ya entró en el contrato en el [[proyecto:04-api-first|hito 04]] con las reglas de estado del catálogo; aquí se repite el patrón con el **stock insuficiente**, el primer error de estado del negocio de pedidos. El **400** de formato existe desde el [[proyecto:05-validacion|hito 05]]. El campo ''userId'' de ''OrderRequest'' es **provisional**: no hay usuarios todavía y el comprador se indica en el cuerpo. En el [[proyecto:08-login-jwt|hito 08]] la identidad vendrá del token y el campo desaparecerá del contrato. ===== 2. Reorganización en módulos ===== - Reorganizar los paquetes por módulo de negocio (con ''git mv'', para conservar el historial): el catálogo queda dentro de ''shop.catalog''; el pedido nace directamente en ''shop.orders''; la raíz de composición — ''ShopApplication'' y la configuración de arranque — queda en el paquete base. - **API pública del catálogo**: la interfaz ''CatalogApi'' del esquema de la [[proyecto:02-especificacion|especificación]], expuesta en ''catalog.api''. Por ella cruzan al pedido las capacidades que necesita — reservar las unidades de una línea, dejando capturado el precio del momento, y liberar las reservadas al cancelar —, con sus datos y sus errores. La forma exacta de esa API — firmas, datos, qué excepciones cruzan el límite — es diseño de cada quien; lo fijo es el resultado: si una línea falla, el stock queda como estaba y no se crea nada. - El interior de cada módulo queda protegido; lo único que el pedido puede consumir del catálogo es su API pública. Lo que cruza el límite forma parte del contrato público del módulo del catálogo; las excepciones de su dominio interno no salen de él (sección 4). - El interior de ''orders'' lo compone cada quien con las piezas del [[temario:04-modulos-modulith|tema 04]] — REST propio, casos de uso, servicio y persistencia **JDBC** —. Las tablas ''orders'' y ''order_lines'' de la especificación entran en el ''init.sql'' (reiniciar el volumen de MariaDB para recargar la semilla); el precio de cada línea se guarda capturado (''unit_price'' de la especificación). ===== 3. Reservar stock, sin transacciones ===== * Al crear el pedido, todas las líneas reservan su stock por la API pública del catálogo; el pedido y sus líneas se guardan con los precios capturados. * Todavía **sin transacciones**: sostener la garantía del contrato — si una línea falla, el stock queda como estaba y no se crea el pedido — con **JDBC** suelto obliga a componer la protección a mano, y el resultado es frágil: si persistir el pedido falla después de reservar, el stock queda reservado sin pedido. Es el punto que el [[proyecto:07-spring-jpa|hito 07]] resolverá con transacciones; el fallo servirá de pista para valorarlas. * Cancelar solo cabe desde ''CREATED''; cancelar un pedido ya cancelado es un estado imposible del dominio de pedidos, que responde 409. También se mapean los errores del catálogo que llegan al pedido: 404 para producto desconocido, 409 para stock insuficiente. ===== 4. Verificación de límites ===== La verificación de módulos pasa a correr con cada build, según el [[temario:04-modulos-modulith|tema 04]]: los límites entre módulos con **Spring Modulith** y las reglas de capas dentro de cada módulo con **ArchUnit**. Opcional: generar la documentación de módulos con el ''Documenter'' del tema. ===== 5. Pruebas ===== * **Módulo orders** (aislado, con la API pública del catálogo doblada): crear un pedido reserva el stock de todas las líneas y persiste el pedido; si una línea falla, no queda nada; cancelar libera el stock y pasa el estado a ''CANCELLED''; cancelar dos veces falla. * **Módulo catalog** (aislado): la reserva y la liberación por la API pública mantienen el stock coherente. * **Presentación de pedidos** (con los módulos doblados): estados 201 / 200 / 404 / 409 según contrato. * Lo de los hitos anteriores sigue verde: catálogos y productos no cambian de comportamiento con la reorganización. ===== Comprobación ===== * La verificación de módulos y las reglas de capas pasan en cada build. * Los pedidos funcionan de punta a punta: crear, consultar y cancelar desde Swagger UI, con el stock reservado y liberado de verdad. * Un pedido con una línea sin stock responde 409 y no deja nada reservado. * ''orders'' solo consume la API pública del catálogo: lo comprueba la verificación, no la buena voluntad. * Contrato en v0.2.0; commit y tag ''hito-06''. **Siguiente hito:** [[proyecto:07-spring-jpa|hito 07 — Persistencia con JPA]].