Capa de interoperabilidad · Prueba técnica de arquitectura

ADR-015: order-service valida el precio con su propia vista de precios

Aceptado el 10/11/2026 por Andrés. Complementado por ADR-020 (vigencias del precio; la ventana de tolerancia y la corrección no cambian).

Contexto

docs/to-be/04-saga-crear-orden.md dice que la capa es la autoridad del precio: una orden se acepta si su unitPrice coincide con el precio vigente o con uno que estuvo vigente dentro de una ventana de tolerancia; si no, 422 PRICE_MISMATCH. El diseño no decía de dónde saca order-service ese precio, y las reglas de arquitectura cierran las salidas obvias:

Además la ventana de 15 minutos no tenía sustento. No absorbe el retraso del dato (que el supuesto 6 fija en un minuto como máximo), sino el tiempo entre que el canal muestra el precio y envía la orden: es una política comercial de protección de precio.

Opciones

Opción A favor En contra
A. order-service consume andes.pricing.prices.v1 y guarda su vista con historial Respeta las reglas; la validación no depende de otro servicio en línea Una tabla más en order-service; la vista es eventualmente consistente
B. gRPC de order-service a pricing-service Una sola fuente en línea Rompe la regla 2 y suma una dependencia síncrona a crear orden
C. Leer el Redis de pricing-service Sin copia Rompe la regla 6; Redis no guarda historial
D. Confiar en el precio del canal Sin trabajo La capa deja de ser la autoridad del precio; un partner puede vender a cualquier precio
E. Incluir el precio en la respuesta de Reserve Reusa la llamada que ya existe Mete precios en inventory-service, que no es su dominio

Decisión

Opción A.

  1. order-service consume andes.pricing.prices.v1 con su propio grupo (order-service) y guarda la tabla price_view en su base: llave sku|channel|priceList, priceVersion, salePrice, currency, momento en que la versión llegó y momento en que fue reemplazada. Deduplica por ce_id, como todo consumidor.
  2. La ventana de tolerancia es igual al vencimiento de la reserva del canal (docs/to-be/03-stock-vendible-y-reservas.md): 15 minutos por defecto, configurable por canal en la misma política. Un solo concepto, la ventana de pago: mientras la reserva sigue viva, el stock y el precio quedan sostenidos el mismo tiempo. Un canal con un pago más largo sube los dos juntos.
  3. Se acepta la orden si unitPrice coincide con la versión vigente, o con una versión reemplazada hace menos que la ventana del canal. Si la orden trae priceVersion, se compara esa versión.
  4. Corrección de precio: el evento PriceChanged gana el campo opcional correction (booleano, false por defecto). Si llega true, las versiones anteriores de esa llave dejan de aceptarse de inmediato, aunque estén dentro de la ventana. Cubre el precio mal cargado a la baja, que de otro modo se seguiría vendiendo durante toda la ventana. El erp-adapter lo marca según el motivo del cambio en el ERP (supuesto 15).
  5. SKU sin precio en la vista: 422 UNKNOWN_SKU.
  6. Las versiones reemplazadas hace más que la ventana más larga de los canales se borran.
  7. Reconstrucción: el tópico es compactado, así que al releerlo desde cero solo queda la última versión de cada llave. Para que el historial dentro de la ventana sobreviva, el canal declara min.compaction.lag.ms de una hora en sus bindings (ADR-017). La ventana de un canal no puede pasar de una hora.

Es un cambio compatible del contrato: un campo opcional nuevo y un consumidor más.

Verificación

Se construye en F4, con order-service:

Qué se sacrifica