← Arquitectura por épica

FT-ARQ-02 · Bitácora inmutable y versionado de producto, plantilla y…

Arquitectura del Nuevo PIM por épica · corte del 18-oct-2026

Propuesta del TL: no sustituye la spec de cada épica

Actualizado el 18-oct-2026. Primera versión de la arquitectura de esta épica.

Qué responde. Cómo funciona por dentro FT-ARQ-02: qué piezas participan, en qué orden hablan, qué APIs hay que construir con su request y su response, y qué le entrega a la épica siguiente.

Para quién. Los equipos que van a construir la épica, QA y arquitectura.

Estado. [Propuesta] del TL del Nuevo PIM, 18-oct-2026, construida sobre ADR-12. La spec de cada épica dice el qué; estas páginas, el cómo.

Fuente. docs/arquitectura/por-epica/epicas/FT-ARQ-02.json, generada con generar.py; la ficha de la épica es trazabilidad/features/FT-ARQ-02-bitacora-inmutable-y-versionado.md.

En 30 segundos

Hoy P360 guarda 52 millones de renglones de bitácora sin el valor anterior ni el nuevo, y un repoblamiento sólo se pudo revertir de tres días. Esta épica hace de Auditoría (MS-05) la bitácora única: recibe los eventos de todos los módulos y guarda, por objeto y campo, el antes y el después, quién, cuándo, desde dónde y en qué ejecución, sin que nada se pueda cambiar ni borrar. Las versiones completas del producto las guarda Producto, para compararlas y restaurarlas.

  • Módulo dueño: MS-05 Auditoría. También participan: MS-01 Catálogo, MS-02 Producto, MS-13 Identidad y acceso, MS-07 Ingesta de archivos, MS-03 Migración y convivencia.
  • Etapa T · Transversal. Recibe de otras épicas y entrega «PE-T · Usuario con permisos y bitácora».
  • APIs: construye 10 y usa 6 que construyen otras épicas.
  • Eventos: publica 1 y consume 17.

1. Dónde encaja

Recibe

DeQué
FT-USR-01 · Identidad y autenticación de usuarios internos…El actor de cada evento (ID, rol y área) tomado del token y del directorio.
FT-FLU-01 · Motor de estados configurable con dos carreteras y reglas…Las transiciones y tareas del motor, que llegan como STATUS_CHANGED y TASK_CHANGED para guardarse.
FT-MIG-03 · Migración inicial de productos desde P360La versión 1 de cada producto migrado, con su corrida, y la historia de estado de P360 y Omega (API-05-006).

Entrega

AQué
PE-T · Usuario con permisos y bitácoraCada cambio de cualquier módulo queda en la bitácora con antes, después, actor, origen, solicitud y ejecución, sin poder cambiarse.
FT-PLT-01 · Fundamentos de gobierno de plantillasLa historia por campo de plantillas, atributos y listas, y la comparación de sus versiones.
FT-CAT-01 · Administración del árbol de categorías webLa auditoría de categorías: cada cambio y publicación con usuario, y la comparación de dos versiones.
FT-RAC-06 · Métricas de tiempos por paso y clasificación de erroresLa historia de estado, incluida la migrada, para medir los pasos anteriores a la salida.
FT-RAC-04 · Consistencia entre sistemas y gobierno de datos proactivoEl origen de cada valor, para explicar una diferencia entre sistemas.
FT-INT-07 · Repoblamiento masivo de productos hacia la tiendaQué productos y campos cambió cada corrida de repoblamiento, para revisarla y revertirla como otra ejecución.

2. Diagrama de contexto

Quién participa y cómo se conecta. Las pantallas llegan por el BFF del portal, que valida el token y compone; cada módulo vuelve a validar el token y decide sus permisos con los roles de MS-13.

🟩 Verde: nuevo, se construye · 🟦 Azul: ya existe y se queda, se reutiliza su código · 🟧 Naranja: temporal o se retira (se apaga en R3) · ⬜ Gris: sistema externo · ★ módulo dueño

Nuevo PIM · un solo sistema

Pantallas y sistemas

Personas

Historia de un objeto
Mis comprobantes

Confirmar un plan del catálogo

TEMPLATE_CHANGED

TEMPLATE_CHANGED
PRODUCT_UPDATED

PRODUCT_UPDATED

Historia de un objeto
Comparar dos versiones desde la bitác…
y 2 más

Nombres visibles

GET /api/v2/products/{id}/versions
GET /api/v2/products/{id}/versions/co…
y 3 más

Carga de historia migrada

Archivos recibidos de SAP

permisos

Administración del catálogo

Gobierno de Datos

Repoblamiento

Entrada Única · Vendor Center

BFF del portal

MS-01 Catálogo

MS-02 Producto

★ MS-05 Auditoría

MS-13 Identidad y acceso

MS-07 Ingesta de archivos

MS-03 Migración y convivencia

Base pim

Eventos

Contexto de FT-ARQ-02

3. Diagrama de flujo

El camino principal, paso por paso. Cuando una pantalla llama a un módulo se ven dos flechas: la pantalla al BFF y el BFF al módulo. Los eventos salen del outbox del módulo después de guardar; quien los consume ignora los repetidos.

MS-13 Identidad y accesoMS-02 ProductoBase·pimMS-05 AuditoríaEventosMS-01 CatálogoEntrada Única · Vendor CenterMS-13 Identidad y accesoMS-02 ProductoBase·pimMS-05 AuditoríaEventosMS-01 CatálogoEntrada Única · Vendor CenterAdministración del catálogoBFFGobierno de Datos1. Cambia la guía de nombre de la plantilla EU4-04122. Confirma el plan del cambioPOST /api/v2/catalog-plans/{planId}/apply3. Avisa el cambio con antes y despuésTEMPLATE_CHANGED4. Entrega el cambio a la bitácoraTEMPLATE_CHANGED5. Guarda una entrada por objeto y campo, sin borrar6. Avisa un cambio de producto con su origenPRODUCT_UPDATED7. Entrega el cambio, descarta repetidos y versiones viejasPRODUCT_UPDATED8. Abre la historia del producto9. Pide la historia compuestaGET /bff/v1/history/{entityType}/{entityId}10. Trae los cambios por campoGET /api/v2/audit/entities/{entityType}/{entityId}/events11. Pone el nombre visible de cada actorGET /api/v2/directory/display-names12. Trae la lista de versiones del productoGET /api/v2/products/{id}/versionsAdministración del catálogoBFFGobierno de Datos
Flujo principal de FT-ARQ-02
Los pasos en texto
PasoDeAQué haceAPI o evento
1Administración del catálogoEntrada Única · Vendor CenterCambia la guía de nombre de la plantilla EU4-0412—
2Entrada Única · Vendor CenterMS-01 Catálogo (por el BFF)Confirma el plan del cambioAPI-01-004
3MS-01 CatálogoEventosAvisa el cambio con antes y despuéscatalog.governance.changed/TEMPLATE_CHANGED
4EventosMS-05 AuditoríaEntrega el cambio a la bitácoracatalog.governance.changed/TEMPLATE_CHANGED
5MS-05 AuditoríaBase pimGuarda una entrada por objeto y campo, sin borrar—
6MS-02 ProductoEventosAvisa un cambio de producto con su origencatalog.product.changed/PRODUCT_UPDATED
7EventosMS-05 AuditoríaEntrega el cambio; descarta repetidos y versiones viejascatalog.product.changed/PRODUCT_UPDATED
8Gobierno de DatosEntrada Única · Vendor CenterAbre la historia del producto—
9Entrada Única · Vendor CenterBFF del portalPide la historia compuestaAPI-BFF-507
10BFF del portalMS-05 AuditoríaTrae los cambios por campoAPI-05-001
11BFF del portalMS-13 Identidad y accesoPone el nombre visible de cada actorAPI-13-018
12BFF del portalMS-02 ProductoTrae la lista de versiones del productoAPI-02-115

3.1 Comparar y restaurar una versión

MS-05 AuditoríaEventosMS-02 ProductoEntrada Única · Vendor CenterMS-05 AuditoríaEventosMS-02 ProductoEntrada Única · Vendor CenterGobierno de DatosBFF1. Elige las versiones 1 y 12 con nombre y material2. Compara las dos versiones del productoGET /api/v2/products/{id}/versions/compare?from=&to=&attributes=3. Pide restaurar la versión 34. Pide el plan de la restauraciónPOST /api/v2/products/{id}/versions/restore/plans5. Confirma el planPOST /api/v2/product-plans/{planId}/apply6. Avisa la versión nueva con origen restauraciónPRODUCT_UPDATED7. Entrega el cambio a la bitácoraPRODUCT_UPDATED8. Compara dos versiones de una categoríaGET /api/v2/audit/entities/{entityType}/{entityId}/versions/com…Gobierno de DatosBFF
Comparar y restaurar una versión

3.2 Revertir una ejecución masiva

EventosMS-02 ProductoMS-05 AuditoríaEntrada Única · Vendor CenterEventosMS-02 ProductoMS-05 AuditoríaEntrada Única · Vendor CenterRepoblamientoBFF1. Busca la corrida de repoblamiento del 23-sep2. Lista productos y campos que cambióGET /api/v2/audit/search3. Pide el plan para revertirlaPOST /api/v2/publications/{jobId}/revert/plans4. Confirma la reversaPOST /api/v2/product-plans/{planId}/apply5. Publica los cambios con su ejecución nuevaPRODUCT_UPDATED6. Guarda la reversa como otra ejecuciónPRODUCT_UPDATEDRepoblamientoBFF
Revertir una ejecución masiva

3.3 Revisar una incidencia de días anteriores

MS-07 Ingesta de archivosEntrada Única · Vendor CenterMS-05 AuditoríaMS-03 Migración y convivenciaMS-07 Ingesta de archivosEntrada Única · Vendor CenterMS-05 AuditoríaMS-03 Migración y convivenciaGobierno de DatosBFF1. Carga la versión 1 migrada y la historia de estadoPOST /internal/audit/bulk-load/status-history2. Busca qué pasó con un producto hace semanas3. Busca cambios por fecha, origen y actorGET /api/v2/audit/search4. Revisa la historia de estado, con lo migradoGET /api/v2/audit/entities/{entityType}/{entityId}/status-histo…5. Revisa los archivos de SAP de esos díasGET /api/v2/sap-ingest/files?interface=&state=6. Abre los comprobantes de esas operacionesGET /bff/v1/receiptsGobierno de DatosBFF
Revisar una incidencia de días anteriores

4. Si algo sale mal

  • Alguien intenta cambiar o borrar una entrada de la bitácora. No existe ninguna ruta para hacerlo: la API responde 405 y el intento queda como evento de seguridad; en la base, ninguna cuenta puede actualizar ni borrar en audit_events.
  • Llega un evento repetido o fuera de orden. MS-05 lo descarta por su eventId y por su versión; lo que no puede leer va a la cola de mensajes muertos y Operación lo reprocesa.
  • MS-05 se atrasa o se cae. Ningún módulo espera a la bitácora: sus eventos se quedan en el bus de eventos y MS-05 se pone al día al volver. La historia muestra «al día hasta» con la hora del último evento guardado.
  • Se revierte una corrida y algún producto cambió después. La vista previa marca ese producto como conflicto y no lo toca; se revierte lo demás y quien revierte decide el resto a mano.
  • Se pide restaurar la versión vigente. MS-02 responde 422 BUSINESS_ERROR: no hay nada que restaurar y no se crea versión.

5. APIs

Las que construye esta épica (10)

GET /api/v2/audit/entities/{entityType}/{entityId}/events · Historia de un objeto
CampoValor
APIAPI-05-001 · MS-05 Auditoría
Para quéLa historia de un producto, variante, plantilla, atributo, lista, categoría, usuario o tercero, campo por campo: valor anterior y nuevo, quién (ID, rol y área), cuándo, desde dónde, en qué solicitud y en qué ejecución. Los estados salen como «Etiqueta (código)».
En esta épicaLa historia por campo de cualquier objeto
Quién la llamaBFF del portal
PermisoENT-L para productos (proveedores y sellers, sólo los suyos), CAT-L para catálogo, ACCESS-ADMIN para usuarios y terceros
CabecerasAuthorization, X-Request-Id
ParámetrosentityType = PRODUCT, entityId = PRD-0004812, field = attributes.MATERIAL, from = 2026-09-01, to = 2026-10-18, page = 1, size = 50
También la usanFT-CAT-01, FT-INT-07, FT-PLT-01, FT-PRD-05

Request

null

Response · 200

{
  "success": true,
  "data": {
    "items": [
      {
        "eventId": "7f1c2a9e-0b44-4c55-9a51-2f7d1b8e6c10",
        "occurredAt": "2026-10-16T18:02:11Z",
        "version": 14,
        "field": "attributes.MATERIAL",
        "before": "Plástico",
        "after": "Acero inoxidable",
        "actor": {
          "userId": "USR-004410",
          "role": "PROVEEDOR",
          "area": null,
          "type": "SUPPLIER"
        },
        "origin": "PORTAL",
        "requestId": "req-55e0c2",
        "jobId": null,
        "sourceModule": "MS-02",
        "eventType": "PRODUCT_UPDATED"
      }
    ],
    "page": 1,
    "size": 50,
    "total": 1,
    "upToDateUntil": "2026-10-18T15:03:59Z"
  }
}

Errores

HTTPCódigoCuándo
404NOT_FOUNDNo hay historia de ese objeto o no es visible para quien llama
403AUTHORIZATION_ERRORQuien llama no puede ver ese tipo de objeto
GET /api/v2/audit/entities/{entityType}/{entityId}/status-history · Historia de estado
CampoValor
APIAPI-05-002 · MS-05 Auditoría
Para quéTodos los cambios de estado de un producto con su transición, quién, cuándo, desde dónde y con qué precisión, incluida la historia que se migró de P360 y Omega.
En esta épicaLa historia de estado, incluida la migrada
Quién la llamaBFF del portal, MS-04 Reportes
PermisoENT-L (proveedores y sellers, sólo sus productos); la cuenta de servicio de MS-04
CabecerasAuthorization, X-Request-Id
ParámetrosentityType = PRODUCT, entityId = PRD-0004812, page = 1, size = 50
También la usanFT-PRD-02, FT-RAC-06

Request

null

Response · 200

{
  "success": true,
  "data": {
    "items": [
      {
        "occurredAt": "2027-02-14T00:00:00Z",
        "from": null,
        "to": "En revisión (EN_REVISION)",
        "legacyCode": 1001,
        "origin": "MIGRATION",
        "runId": "run-status-0007",
        "precision": "DAY",
        "actor": null
      },
      {
        "occurredAt": "2027-02-20T16:30:02Z",
        "from": "En revisión (EN_REVISION)",
        "to": "Creación de SKU (CREACION_SKU)",
        "transition": "T05",
        "origin": "PORTAL",
        "precision": "EXACT",
        "actor": {
          "userId": "USR-000231",
          "role": "COMPRAS",
          "area": "COMPRAS-HOGAR"
        }
      }
    ],
    "page": 1,
    "size": 50,
    "total": 2
  }
}

Errores

HTTPCódigoCuándo
404NOT_FOUNDEl producto no existe o no es visible para quien llama
GET /api/v2/audit/search · Buscar en la bitácora
CampoValor
APIAPI-05-003 · MS-05 Auditoría
Para quéBusca cambios por actor, fechas, tipo de objeto, origen, solicitud o ejecución. Con jobId lista qué objetos y campos cambió un proceso masivo, para revisarlo o revertirlo.
En esta épicaBuscar por actor, fechas, origen, solicitud o ejecución
Quién la llamaBFF del portal
PermisoAUDIT-L (Gobierno de Datos y de Producto); el dueño de una ejecución ve la suya y cada usuario ve sus propias operaciones
CabecerasAuthorization, X-Request-Id
Parámetrosactor = `, from = 2026-09-23, to = 2026-09-24, entityType = PRODUCT, origin = BULK, requestId = , jobId = job-rep-0923, page = 1, size = 200`

Request

null

Response · 200

{
  "success": true,
  "data": {
    "summary": {
      "jobId": "job-rep-0923",
      "objects": 861,
      "fields": 1722,
      "startedAt": "2026-09-23T03:00:05Z",
      "finishedAt": "2026-09-23T04:12:40Z"
    },
    "items": [
      {
        "entityType": "PRODUCT",
        "entityId": "PRD-0007710",
        "version": 9,
        "field": "classification.webCategoryIds",
        "before": [
          "catst-000812"
        ],
        "after": [],
        "occurredAt": "2026-09-23T03:00:09Z",
        "origin": "BULK",
        "requestId": "req-0923a1",
        "jobId": "job-rep-0923"
      }
    ],
    "page": 1,
    "size": 200,
    "total": 1722
  }
}

Errores

HTTPCódigoCuándo
400VALIDATION_ERRORNo trae ningún filtro o el periodo pasa de 400 días sin jobId ni objeto
403AUTHORIZATION_ERRORBusca operaciones de otros sin AUDIT-L
POST /internal/audit/bulk-load/status-history · Carga de historia migrada
CampoValor
APIAPI-05-006 · MS-05 Auditoría
Para quéMS-03 carga, por lotes, la entrada inicial de cada objeto migrado (versión 1, origen MIGRATION y su corrida) y la historia de estado de P360 y Omega con su precisión.
En esta épicaCargar la versión 1 migrada y la historia de estado
Quién la llamaMS-03 Migración y convivencia
PermisoSólo la cuenta de servicio de MS-03 (MIG-ADMIN)
CabecerasAuthorization, X-Request-Id, Idempotency-Key
Escribe enaudit_events
También la usanFT-MIG-03, FT-MIG-04

Request

{
  "runId": "run-status-0007",
  "records": [
    {
      "entityType": "PRODUCT",
      "entityId": "PRD-0004812",
      "baseline": {
        "version": 1,
        "origin": "MIGRATION",
        "at": "2027-02-14T05:00:00Z"
      },
      "statusHistory": [
        {
          "at": "2026-08-30T00:00:00Z",
          "from": null,
          "legacyCode": 1001,
          "to": "EN_REVISION",
          "precision": "DAY",
          "source": "P360"
        }
      ]
    }
  ]
}

Response · 200

{
  "success": true,
  "data": {
    "runId": "run-status-0007",
    "stored": 2,
    "duplicates": 0,
    "rejected": []
  }
}

Errores

HTTPCódigoCuándo
409IDEMPOTENCY_MISMATCHEl mismo lote llega otra vez con contenido distinto
422VALIDATION_ERRORUn registro trae un estado o un código de P360 sin equivalencia
GET /api/v2/audit/entities/{entityType}/{entityId}/versions/compare · Comparar dos versiones desde la bitácora
CampoValor
APIAPI-05-008 · MS-05 Auditoría
Para quéCompara dos versiones de una plantilla, atributo, lista o categoría con su cadena de cambios: qué campos cambiaron entre las dos, el valor en cada una y la fecha y el usuario de cada versión. Los productos se comparan con sus fotos en MS-02.
En esta épicaComparar dos versiones de una plantilla o categoría desde la bitácora
Quién la llamaBFF del portal
PermisoCAT-L
CabecerasAuthorization, X-Request-Id
ParámetrosentityType = CATEGORY, entityId = catst-000812, from = 3, to = 7, fields = name.es,parent,status

Request

null

Response · 200

{
  "success": true,
  "data": {
    "entityType": "CATEGORY",
    "entityId": "catst-000812",
    "from": {
      "version": 3,
      "at": "2027-03-02T17:00:00Z",
      "actor": {
        "userId": "USR-000502",
        "role": "CATEGORY"
      }
    },
    "to": {
      "version": 7,
      "at": "2027-05-11T14:20:00Z",
      "actor": {
        "userId": "USR-000503",
        "role": "CATEGORY"
      }
    },
    "fields": [
      {
        "field": "name.es",
        "atFrom": "Licuadoras",
        "atTo": "Licuadoras y batidoras",
        "changed": true
      },
      {
        "field": "parent",
        "atFrom": "catst-000800",
        "atTo": "catst-000800",
        "changed": false
      },
      {
        "field": "status",
        "atFrom": null,
        "atTo": null,
        "changed": false,
        "note": "Sin dato: no cambió desde la migración y la migración no lo trajo"
      }
    ]
  }
}

Errores

HTTPCódigoCuándo
404NOT_FOUNDEl objeto o alguna de las versiones no existe
422BUSINESS_ERRORSe pide comparar un producto: sus versiones son de MS-02
GET /bff/v1/history/{entityType}/{entityId} · Historia de un objeto
CampoValor
APIAPI-BFF-507 · BFF del portal
Para quéLa pantalla de historia: los cambios por campo de la bitácora (MS-05), con el nombre visible de cada actor (MS-13) y, si es producto, sus versiones (MS-02). A proveedores y sellers les muestra el área, no el nombre.
En esta épicaLa pantalla de historia: bitácora, nombres y versiones
Quién la llamaEntrada Única · Vendor Center (por el BFF)
PermisoEl mismo que da cada módulo para ver el objeto (ENT-L, CAT-L o el dueño del producto)
CabecerasX-Request-Id
ParámetrosentityType = PRODUCT, entityId = PRD-0004812, field = `, page = 1`

Request

null

Response · 200

{
  "success": true,
  "data": {
    "entity": {
      "type": "PRODUCT",
      "id": "PRD-0004812",
      "currentVersion": 14
    },
    "events": [
      {
        "at": "2026-10-16T18:02:11Z",
        "version": 14,
        "field": "attributes.MATERIAL",
        "before": "Plástico",
        "after": "Acero inoxidable",
        "actor": {
          "displayName": "Proveedor Demo 01",
          "area": null,
          "type": "SUPPLIER"
        },
        "origin": "PORTAL",
        "requestId": "req-55e0c2",
        "jobId": null
      },
      {
        "at": "2026-10-15T09:40:00Z",
        "version": 13,
        "field": "attributes.TITULO",
        "before": "Licuadora 10 vel",
        "after": "Licuadora de 10 velocidades",
        "actor": {
          "displayName": null,
          "area": "IA",
          "type": "SYSTEM"
        },
        "origin": "IA",
        "requestId": "req-1b77a0",
        "jobId": "job-ia-3301"
      }
    ],
    "versions": [
      {
        "version": 14,
        "at": "2026-10-16T18:02:11Z"
      },
      {
        "version": 13,
        "at": "2026-10-15T09:40:00Z"
      }
    ],
    "upToDateUntil": "2026-10-18T15:03:59Z"
  }
}

Errores

HTTPCódigoCuándo
404NOT_FOUNDEl objeto no existe o no es visible para quien llama
GET /bff/v1/receipts · Mis comprobantes
CampoValor
APIAPI-BFF-508 · BFF del portal
Para quéLa pantalla de comprobantes: las operaciones que hizo el usuario en un periodo, de cualquier módulo, tomadas de la bitácora (MS-05), y el recibo de cada una pedido a su módulo dueño, aunque se haya perdido la respuesta.
En esta épicaLa pantalla de comprobantes de todos los módulos
Quién la llamaEntrada Única · Vendor Center (por el BFF)
PermisoEl propio usuario; AUDIT-L para ver las de otro
CabecerasX-Request-Id
Parámetrosfrom = 2026-10-16, to = 2026-10-18, requestId = ``

Request

null

Response · 200

{
  "success": true,
  "data": {
    "items": [
      {
        "requestId": "req-55e0c2",
        "module": "MS-02",
        "at": "2026-10-18T15:04:05Z",
        "objects": 1,
        "status": "DONE",
        "receiptPath": "/api/v2/product-receipts/req-55e0c2"
      },
      {
        "requestId": "req-3f90d1",
        "module": "MS-13",
        "at": "2026-10-18T15:04:07Z",
        "objects": 1,
        "status": "DONE",
        "receiptPath": "/api/v2/identity-receipts/req-3f90d1"
      }
    ],
    "page": 1,
    "size": 50,
    "total": 2
  }
}

Errores

HTTPCódigoCuándo
400VALIDATION_ERROREl periodo pasa de 90 días
GET /api/v2/products/{id}/versions · Lista las versiones completas de un producto (product_versio
CampoValor
APIAPI-02-115 · MS-02 Producto
Para quéLista las versiones completas de un producto (product_versions) con su número, fecha, usuario, origen, motivo (edición, aprobación, restauración o migración) y la solicitud o ejecución que la creó.
En esta épicaLista de versiones de un producto

Request

null

Response · 200

{
  "success": true,
  "data": {
    "productId": "PRD-0004812",
    "current": 14,
    "items": [
      {
        "version": 14,
        "at": "2026-10-16T18:02:11Z",
        "actor": {
          "userId": "USR-004410",
          "role": "PROVEEDOR"
        },
        "origin": "PORTAL",
        "reason": "EDIT",
        "requestId": "req-55e0c2",
        "jobId": null
      },
      {
        "version": 1,
        "at": "2027-02-14T05:00:00Z",
        "actor": null,
        "origin": "MIGRATION",
        "reason": "MIGRATION",
        "runId": "run-prd-0003"
      }
    ],
    "page": 1,
    "size": 50,
    "total": 14
  }
}

Errores

HTTPCódigoCuándo
403AUTHORIZATION_ERRORQuien llama no tiene el permiso o no es el módulo autorizado
404NOT_FOUNDNo existe lo que se pide o no es visible para quien llama
GET /api/v2/products/{id}/versions/compare?from=&to=&attributes= · Compara dos versiones cualesquiera de un producto con sus fo
CampoValor
APIAPI-02-116 · MS-02 Producto
Para quéCompara dos versiones cualesquiera de un producto con sus fotos completas y sólo con los atributos elegidos: el valor en cada una, si cambió, y la fecha y el usuario de cada versión. Debe responder en el mismo tiempo con 14 o con 1 versión.
En esta épicaComparar dos versiones de un producto por atributos elegidos

Request

null

Response · 200

{
  "success": true,
  "data": {
    "productId": "PRD-0004812",
    "from": {
      "version": 1,
      "at": "2027-02-14T05:00:00Z",
      "origin": "MIGRATION"
    },
    "to": {
      "version": 12,
      "at": "2027-04-02T17:15:00Z",
      "actor": {
        "userId": "USR-004410",
        "role": "PROVEEDOR"
      }
    },
    "attributes": [
      {
        "attribute": "NOMBRE",
        "atFrom": "Licuadora 10 vel",
        "atTo": "Licuadora de 10 velocidades",
        "changed": true
      },
      {
        "attribute": "MATERIAL",
        "atFrom": "Acero inoxidable",
        "atTo": "Acero inoxidable",
        "changed": false
      }
    ]
  }
}

Errores

HTTPCódigoCuándo
403AUTHORIZATION_ERRORQuien llama no tiene el permiso o no es el módulo autorizado
404NOT_FOUNDNo existe lo que se pide o no es visible para quien llama
POST /api/v2/products/{id}/versions/restore/plans · Prepara la restauración de una versión anterior: dice qué va
CampoValor
APIAPI-02-117 · MS-02 Producto
Para quéPrepara la restauración de una versión anterior: dice qué valores y relaciones vuelven y crea, al confirmar con API-02-057, una versión nueva con esos valores; nunca borra historia ni restaura la vigente.
En esta épicaRestaurar una versión anterior del producto

Request

{
  "version": 3,
  "currentVersion": 14,
  "reason": "El proveedor cambió el material por error"
}

Response · 200

{
  "success": true,
  "data": {
    "planId": "pln-rs1402",
    "expiresAt": "2026-10-18T15:14:05Z",
    "productId": "PRD-0004812",
    "restores": 3,
    "createsVersion": 15,
    "changes": [
      {
        "path": "attributes.MATERIAL",
        "before": "Acero inoxidable",
        "after": "Plástico"
      }
    ],
    "applyUrl": "/api/v2/product-plans/pln-rs1402/apply"
  }
}

Errores

HTTPCódigoCuándo
403AUTHORIZATION_ERRORQuien llama no tiene el permiso o no es el módulo autorizado
422VALIDATION_ERRORFalta un dato o viene con un formato inválido; el detalle dice qué campo
409IDEMPOTENCY_MISMATCHSe repite la llave de idempotencia con otro cuerpo

Las que usa y construye otra épica (6)

Su request y su response están en la página de la épica que la construye.

APIMétodo y rutaPara qué, aquíLa construye
API-05-007POST /internal/audit/security-eventsRegistrar los intentos de cambiar o borrar la bitácoraFT-USR-02 · Matriz de roles y permisos por módulo, acción, negocio y…
API-13-018GET /api/v2/directory/display-namesEl nombre visible de cada actorFT-USR-01 · Identidad y autenticación de usuarios internos…
API-01-004POST /api/v2/catalog-plans/{planId}/applyEjemplo de cambio confirmado en el catálogoFT-PLT-01 · Fundamentos de gobierno de plantillas
API-02-057POST /api/v2/product-plans/{planId}/applyConfirmar la restauración o la reversaFT-FLU-01 · Motor de estados configurable con dos carreteras y reglas…
API-02-403POST /api/v2/publications/{jobId}/revert/plansPreparar la reversa de una corrida, que salta lo que cambió despuésFT-INT-07 · Repoblamiento masivo de productos hacia la tienda
API-07-001GET /api/v2/sap-ingest/files?interface=&state=Los archivos de SAP de días anterioresFT-INT-03 · Entrada de SKUs creados en SAP

6. Eventos

Publica

EventoCuándoQuién lo consume
catalog.product.changed/PRODUCT_UPDATEDAl restaurar una versión o revertir una corrida, MS-02 publica el cambio con su origen y su ejecuciónFT-ACT-05, FT-IA-01, FT-PLT-07, FT-PRD-05, FT-RAC-06

Consume

EventoPara quéQuién lo publica
catalog.governance.changed/TEMPLATE_CHANGEDGuardar por campo los cambios de plantillasFT-IA-04, FT-PLT-01, FT-PLT-05, FT-PLT-06, FT-PLT-09
catalog.governance.changed/ATTRIBUTE_CHANGEDGuardar por campo los cambios de atributosFT-PLT-02, FT-PLT-04, FT-PLT-09
catalog.governance.changed/LOV_VALUE_CHANGEDGuardar los cambios de valores de listaFT-INT-04, FT-PLT-03, FT-PLT-09, FT-REF-01
catalog.governance.changed/CATEGORY_UPSERTEDGuardar los cambios de categoríasFT-CAT-01
catalog.governance.changed/CATEGORY_MOVEDGuardar los movimientos de ramaFT-CAT-01
catalog.product.changed/PRODUCT_UPDATEDGuardar por campo los cambios de producto, con su origenesta misma épica
catalog.product.changed/STATUS_CHANGEDGuardar cada transición de estadoFT-CAT-03, FT-CAT-05, FT-FLU-01, FT-FLU-02, FT-FLU-04, FT-FLU-06, FT-FLU-07, FT-INT-03, FT-INT-05, FT-INT-06, FT-INT-07, FT-PRD-02, FT-PRD-03, FT-PRD-04, FT-PRD-05, FT-PRD-06, FT-PRD-07, FT-PRD-09, FT-PRD-10, FT-RAC-01, FT-RAC-02, FT-RAC-03, FT-RAC-05, FT-REF-02
catalog.product.changed/TASK_CHANGEDGuardar quién abrió, tomó y cerró cada tareaFT-ACT-02, FT-ACT-05, FT-ACT-06, FT-CAT-03, FT-CAT-04, FT-CAT-06, FT-FLU-01, FT-FLU-02, FT-FLU-04, FT-FLU-07, FT-IA-04, FT-IA-06, FT-INT-03, FT-INT-07, FT-PRD-02, FT-PRD-03, FT-PRD-04, FT-PRD-05, FT-PRD-06, FT-PRD-07, FT-PRD-08, FT-RAC-01, FT-RAC-02, FT-RAC-03, FT-RAC-04, FT-RAC-05, FT-REF-03
catalog.product.changed/EAN_CHANGEDGuardar cada alta y liberación de EANFT-FLU-01, FT-INT-04, FT-INT-06, FT-MIG-04, FT-PRD-03, FT-PRD-04, FT-PRD-06, FT-PRD-09
catalog.product.changed/SKU_ASSIGNEDGuardar el SKU que asignó SAP con el 122FT-INT-03, FT-PRD-02
catalog.product.changed/CONFIG_CHANGEDGuardar quién cambió una tabla del motor, incluida la de permisos (ROLE_MATRIX)FT-FLU-01, FT-FLU-07
catalog.delivery.result/DELIVERY_RESULTGuardar el acuse de cada destinoFT-CAT-02, FT-CAT-05, FT-INT-01, FT-INT-02, FT-INT-05, FT-INT-07, FT-PRD-02
catalog.sap.inbound/SAP_MASTER_DATA_CHANGEDGuardar los datos maestros que llegaron de SAP, con SAP como origenFT-INT-04
catalog.governance.changed/ATTRIBUTE_GROUP_CHANGEDGuardar los cambios de grupos de atributos, incluidos los de permisoFT-PLT-04
catalog.asset.changed/ASSET_FROZENGuardar el congelado de cada imagenFT-ACT-02, FT-ACT-03, FT-ACT-04, FT-ACT-06
catalog.identity.changed/ROLE_CHANGEDGuardar los cambios de rolesFT-USR-02
catalog.ai.result/AI_SUGGESTION_READYGuardar qué propuso la IA, como origen distinto del de quien corrigió despuésFT-IA-01, FT-IA-04

7. Datos

Cada colección tiene un solo dueño, el único que escribe en ella.

ColecciónDueñoQué guardaEsta épica
audit_eventsMS-05Quién cambió qué, cuándo, desde dónde, con el antes y el después.escribe
product_versionsMS-02Foto numerada del producto en cada versión.escribe
audit_eventsMS-05Quién cambió qué, cuándo, desde dónde, con el antes y el después.lee

8. Historias que cubre

HistoriaPrioridadCómo la cubre
FT-ARQ-02·H1 · Ver por cada campo cambiado el valor anterior y el nuevo, quién, cuándo y…P1pasos 1, 2, 3, 4, 5, 8, 9, 10, 11; API-05-001, API-05-007
FT-ARQ-02·H2 · Versiones completas del producto y regresar a una anteriorP1pasos 12; flujo «Comparar y restaurar una versión»; API-02-115, API-02-117
FT-ARQ-02·H3 · Saber qué productos cambió cada ejecución y revertirlaP1flujo «Revertir una ejecución masiva»; API-05-003, API-02-403
FT-ARQ-02·H4 · Revisar propuestas, logs y archivos de integración de días anterioresP2flujo «Revisar una incidencia de días anteriores»; API-07-001, API-BFF-508
FT-ARQ-02·H5 · Saber qué sistema originó cada valor y si lo enriqueció la IAP2pasos 6, 7; flujo «Revisar una incidencia de días anteriores»; API-05-006, API-05-002
FT-ARQ-02·H6 · Comparar dos versiones por atributos elegidosP2flujo «Comparar y restaurar una versión»; API-02-116, API-05-008

9. Decisiones de esta épica

Las tomó el TL como arquitecto; quedan como [Propuesta] hasta que el equipo de la épica las revise.

  • D-ARQ-02-1. Una sola bitácora: MS-05 consume todos los tópicos catalog. y guarda una entrada por objeto y campo con antes, después, actor, origen, fecha, versión, solicitud y ejecución. Las bitácoras sueltas que se diseñaron por separado (audit_events de plantillas, category_audit_events con vencimiento, audit-log-service) no se construyen aparte. Por qué:* Había tres diseños de bitácora; un solo dueño evita historias que no cuadran entre sí. Se apoya en PD-05, ADR-12, RF-CAL-12.
  • D-ARQ-02-2. Inmutable de verdad: no existe ninguna ruta para cambiar ni borrar; la cuenta de MS-05 en la base sólo inserta y lee audit_events y ninguna otra cuenta la toca; un intento por la API responde 405 y queda como evento de seguridad. Sin vencimiento automático. Por qué: La ERF pide bitácora inmutable y la traza de categorías de hoy vence a los 90 días. Se apoya en DUD-27, RF-CAL-12.
  • D-ARQ-02-3. La foto completa de cada versión la guarda su dueño, MS-02, en product_versions. Nace versión nueva cuando cambia el usuario, cuando el mismo usuario edita más de 30 minutos después de la última versión, al aprobar y al restaurar. Restaurar crea la versión N+1 con los valores elegidos; no se restaura la vigente y no se borra historia. Por qué: Producto es dueño del producto y de sus versiones; MS-05 guarda la cadena de cambios. El umbral de 30 minutos agrupa una sesión de captura sin perder trabajo de días distintos. Se apoya en DUD-27, RF-CAL-12.
  • D-ARQ-02-4. Plantillas, atributos, listas y categorías no tienen fotos: se comparan con la cadena de cambios de MS-05 (API-05-008), con la fecha y el usuario de cada versión; lo que no cambió desde la migración sale «sin dato» si la migración no lo trajo. Los productos se comparan con las fotos de MS-02. Por qué: No se duplica el catálogo en otra colección y la comparación sale de lo que ya se guarda. Se apoya en RF-CAL-12, PD-05.
  • D-ARQ-02-5. Todo evento lleva en su cuerpo la solicitud (requestId) y, si vino de un proceso masivo, la ejecución (jobId); MS-05 los indexa para listar qué cambió cada ejecución. Revertir es otra ejecución de MS-02 con su propia vista previa (API-02-403 para las corridas), que salta lo que cambió después. Por qué: Hoy sólo se pudieron revertir 3 días de repoblamiento; sin el jobId en el evento no se sabe qué tocó cada corrida. Se apoya en RF-CAL-13, DEC-90, FT-INT-07.
  • D-ARQ-02-6. La bitácora no vence: a los 24 meses se archiva por mes en el almacén con su huella y se consulta en segundo plano, pero nunca se borra. Los archivos de integración (SAP, respuestas de destinos y cargas) se conservan 13 meses, para poder revisar una incidencia de cualquier temporada. Por qué: Las propuestas de días anteriores no se pudieron analizar porque se borraron los archivos; 13 meses cubren una temporada completa más un mes. Se apoya en DUD-27, RF-CAL-13.
  • D-ARQ-02-7. La versión 1 de un producto migrado es la foto de la migración, con origen MIGRATION y su corrida; MS-03 carga en MS-05 esa entrada inicial y la historia de estado con su precisión (API-05-006). La historia de valores arranca en cero; se recomienda no migrar las 52 M filas de P360, que no traen antes ni después. Por qué: Así el primer registro de cada producto dice de dónde vino, y la historia migrada sirve para medir tiempos sin inventar valores. Se apoya en PD-18, DEC-ARQ-09, DEC-PRD-06.
  • D-ARQ-02-8. MS-05 guarda el ID del actor, su rol y su área, no su nombre; el BFF pone el nombre visible al mostrar la historia (API-13-018), y a proveedores y sellers les muestra el área. El origen de cada valor (portal, carga masiva, API de terceros, SAP, IA, migración) va en cada entrada, así se ve cuando la IA propuso y una persona corrigió. Por qué: El nombre es un dato de MS-13 y puede cambiar; copiarlo en la bitácora lo duplicaría. Se apoya en ADR-12, DEC-65.

Qué falta decidir

IDQué falta
PD-18Gobierno de Producto con Arquitectura confirma que la versión 1 es la migración y si se migra algo de las bitácoras de P360 (FT-MIG-03).
DUD-27Arquitectura con Gobierno de Producto ratifica el umbral de versión (30 minutos) y la retención de archivos de integración (13 meses).
PD-01Confirmar que el versionado con restauración entra en la Fase 1, porque P360 no lo tiene.

Fuentes

  1. [D] trazabilidad/features/FT-ARQ-02-bitacora-inmutable-y-versionado.md: la ficha de la épica, con sus historias, reglas y bloqueos.
  2. [D] ADR-12 · Un solo sistema: backend compartido y una sola base (sección 3.4).
  3. [D] docs/arquitectura/por-epica/base/: piezas, colecciones, eventos y el hilo de punta a punta.
  4. [D] docs/arquitectura/omega-a-pim-headless/contratos-api.md: convenciones y endpoints de partida.
  5. [D] docs/arquitectura/maquina-de-estados-to-be/propuesta-maquina-de-estados.md §7: estados, tareas y canales.
  6. [I] Las decisiones de la sección 9 son del TL sobre esas fuentes.