ADR-020: precios con vigencia, el evento lleva el calendario y el precio se elige al leer
Aceptado el 11/11/2026 por Andrés. Complementa ADR-015 (vigencias del precio).
Contexto
Price trae validFrom y validTo, pero el diseño no dice qué hace la capa con un precio de vigencia futura. Es el caso normal de una promoción: el área comercial carga el viernes negro con días de anticipación.
El tópico andes.pricing.prices.v1 es compactado con clave sku|channel|priceList. Si el erp-adapter publica el precio futuro con esa clave, la compactación termina borrando el precio vigente: el consumidor que reconstruye su modelo desde el tópico ve solo el futuro. Y si el servicio aplica cada evento al llegar, la promoción se vende antes de tiempo; si lo guarda para aplicarlo en validFrom, ningún evento llega en ese momento que dispare el cambio.
Así lo resuelven los sistemas de referencia:
- ERP: un precio es un registro con vigencia (en SAP, un registro de condición con fecha desde y hasta). Una promoción es un registro nuevo que se superpone al precio base durante su vigencia, no una modificación del precio base.
- Plataformas de comercio: guardan varios precios por producto y alcance, cada uno con su ventana, y eligen al leer. En commercetools, la selección de precio descarta los que no están vigentes en el momento de la consulta y da prioridad a los que tienen fecha frente a los que no.
Opciones
| Opción | A favor | En contra |
|---|---|---|
| A. Mantener la clave y agregar al evento el calendario del alcance: todas las ventanas no vencidas. El consumidor elige al leer | Compatible con la compactación: el último valor de la clave tiene todo; cambio aditivo en v1 (ADR-011); el orden por clave se conserva y la corrección de ADR-015 sigue igual |
El adaptador debe conocer todas las ventanas del alcance; el evento crece |
B. Una clave por ventana: sku|channel|priceList|validFrom |
Una fila del ERP es un mensaje, como los precios independientes de commercetools | Cambia la clave del tópico: ruptura que pide .v2; las ventanas de un mismo alcance caen en particiones distintas y la corrección de ADR-015 pierde el orden |
C. Guardar vigente y siguiente, y cambiar en validFrom |
Simple de explicar | No cubre el fin de una promoción que vuelve al precio base ni dos cambios programados; sigue perdiendo el vigente con la compactación |
D. Un proceso programado aplica el precio a la hora de validFrom |
El modelo de lectura guarda un solo precio | Una pieza más; si está caído en ese instante, el precio no cambia; con miles de SKU en la misma hora es un pico de escrituras |
Decisión
Opción A.
- Contrato:
Priceagregaschedule, opcional: la lista de ventanas no vencidas del alcancesku|channel|priceList, cada una conlistPrice,salePrice,currency,taxIncluded,priceVersion,validFromyvalidTo(ausente es sin fin). Los campos de primer nivel siguen siendo la ventana vigente al publicar, así un consumidor que ignoraschedulese comporta como hoy. - Regla de selección, en el contrato: entre las ventanas con
validFrom <= ahoray sinvalidToo conahora < validTo, gana la devalidFrommás reciente. Así una promoción con fin gana sobre el precio base mientras dura, y al vencer vuelve el base sin que nadie publique nada. Si ninguna está vigente, el SKU va anotFound. La hora es UTC. - erp-adapter: mantiene en su propia base las ventanas de cada alcance, alimentadas por la foto inicial y por el feed (no consulta al ERP por cada cambio). Ante el cambio de cualquier ventana, publica el calendario completo del alcance y omite las vencidas. Máximo 10 ventanas no vencidas por alcance (supuesto S22); si el ERP trae más, publica las 10 más próximas y alerta.
- pricing-service: guarda el calendario en
price:{sku}:{channel}:{priceList}y aplica la regla al leer. La respuesta deGET /v1/pricesno cambia: lleva la ventana elegida, con suvalidFrom,validToypriceVersion. - order-service (F4): su vista de precios guarda el calendario y aplica la misma regla al momento en que el canal vio el precio. La ventana de tolerancia y la corrección de ADR-015 no cambian: una corrección invalida las versiones anteriores del alcance.
- Mock del ERP: modela cada precio como fila con vigencia, varias por alcance. Un precio programado se inyecta como fila nueva con
validFromfuturo.
Verificación
- Prueba de pricing con reloj controlado: antes de
validFromresponde el precio base; entrevalidFromyvalidTo, la promoción; después devalidTo, otra vez el base. Sin eventos nuevos entre una y otra. - Prueba de reconstrucción: se borra el modelo de lectura, se relee el tópico compactado y el precio elegido es el mismo.
- Prueba del adaptador: una fila futura en el ERP produce un evento con el calendario completo, y el precio vigente sigue en el evento.
- La regla de selección vive en una prueba de contrato compartida por las dos implementaciones (pricing y order) con los mismos casos.
Qué se sacrifica
- El erp-adapter guarda estado de negocio (las ventanas por alcance), no solo su cursor.
- El evento crece con las ventanas futuras; con el tope de 10, sigue en pocos kilobytes.
- La regla de selección se implementa en dos servicios. Se mitiga con la prueba de contrato compartida.
- Un precio sale vigente en el segundo exacto de
validFromsegún el reloj del servicio; una desviación de reloj de unos segundos adelanta o atrasa el cambio en ese tanto.