Skip to content

Versioning vs Audit log — cuándo usar cuál ​

Documento de comparación entre los paquetes arqel-dev/versioning y arqel-dev/audit. Léelo junto con los 3 escenarios reales en esta misma carpeta: CMS Articles, E-commerce Orders, Legal Contracts.

Propósito ​

Ambos paquetes resuelven problemas de "historial", pero resuelven problemas diferentes. Confundirlos lleva a:

  • Almacenamiento explotando un orden de magnitud (snapshots de modelos volátiles).
  • Audit log usado como fuente primaria de restore — perdiendo estado parcial en columnas no auditadas.
  • Compliance fallando porque almacenaste el snapshot pero no quién aprobó el cambio.

Este doc da una regla objetiva para decidir.

TL;DR ​

  • arqel-dev/versioning = snapshot completo del contenido de un registro en un punto en el tiempo. Permite restore. Caso de uso: "rollback del artículo a la versión de ayer".
  • arqel-dev/audit = log de eventos append-only de quién hizo qué y cuándo. No permite restore (solo). Caso de uso: "¿quién cambió el estado de este pedido?".
  • Ambos juntos = compliance / legal-tech / financiero: snapshot para preservar contenido + audit para preservar contexto humano.

Tabla de comparación ​

Aspectoarqel-dev/versioningarqel-dev/audit
Forma de almacenamientoSnapshot completo de getAttributes() por saveFila de evento con event_name + delta
Costo de almacenamientoAlto (lineal en número de saves × tamaño del modelo)Bajo (lineal en número de eventos × tamaño del delta)
Patrón de query"Dame la versión N de este registro""Dame todos los eventos del tipo X entre T1 y T2"
Recovery point-in-timeSí, nativo (restoreToVersion)No — solo reconstrucción manual replicando eventos
Append-only garantizadoSí ($timestamps=false, sin updates)Sí (event log puro)
Atribución de usuarioOpcional (created_by_user_id defensivo)Obligatoria por diseño
Snapshot vs deltaSnapshot (todas las columnas) + diff en changesSolo delta + payload del evento
Capacidad de restoreSí (idempotente, crea una nueva versión)No — los eventos no restauran estado
GDPR right-to-be-forgottenDifícil — el payload puede contener PII; necesita pruneOldVersions o un hook serializadorMás fácil — anonimizar actor_id/payload
Impacto de performance (write)1 INSERT extra + JSON encode del payload completo1 INSERT extra con payload menor
Evolución de schemaTolerante (snapshot es JSON, no atado al schema actual)Tolerante (event_name versionado por convención)
Cardinalidad idealBaja-media (cientos–miles de registros)Cualquiera (millones+)
Tipo de modelo idealContenido editable (artículo, contrato, configuración)Eventos transaccionales discretos (pedido, pago, login)
Costo de retención a largo plazoPuede dominar el storage; necesita prune agresivoBajo; archivado incremental viable
Diff legible para humanosSí, vía changes por fieldIndirecto (necesita replay)

Árbol de decisión ​

mermaid
flowchart TD
    Start([I need to record history]) --> Q1{Do I need to<br/>restore to a<br/>point in time?}
    Q1 -->|Yes| Q2{How many saves<br/>per record per month?}
    Q1 -->|No| Q3{Do I need to know<br/>who made<br/>each change?}
    Q2 -->|< 100| V[arqel-dev/versioning]
    Q2 -->|>= 100 and small payload| V
    Q2 -->|>= 100 and large payload| Q4{Can I filter<br/>volatile fields?}
    Q4 -->|Yes| V
    Q4 -->|No| AB[arqel-dev/audit + manual diff]
    Q3 -->|Yes| A[arqel-dev/audit]
    Q3 -->|No| Q5{Am I in a<br/>regulated<br/>legal/financial domain?}
    Q5 -->|Yes| Both[Versioning + Audit<br/>combined]
    Q5 -->|No| Nothing[Neither]
    V --> Q6{Regulated domain?}
    Q6 -->|Yes| Both
    Q6 -->|No| Done([Done — versioning only])
    A --> Done2([Done — audit only])

Anti-patrones ​

1. Versionar event logs = bloat catastrófico ​

La tabla event_logs recibe 50k inserts/día. Aplicar el trait Versionable a ella inmediatamente duplica el volumen de almacenamiento y cada save dispara prune. Los event logs de auditoría no necesitan ser versionados — ya son un log append-only.

2. Audit log como fuente primaria de restore ​

Tratar de reconstruir un Article a partir de "User edited body at T1" + "User edited title at T2" requiere replay determinista, ordering correcto, y pierde campos no auditados. El audit log responde "qué ocurrió", no "cuál era el estado". Para restore, usa versioning.

3. No filtrar payload sensible (PII) en el snapshot ​

payload en arqel_versions es JSON crudo de getAttributes(). Si el modelo tiene cpf, password_hash, api_token, terminan almacenados en texto plano preservados durante años. Antes de adoptar versioning, decide qué fields remover (idealmente vía el hook serializing del trait, o sobrescribiendo getAttributes() para la versión).

4. Saltarse retention / prune ​

keep_versions=0 en producción sin job de prune = bomba de tiempo. En 6 meses la tabla arqel_versions puede llegar a decenas de GB y dominar el backup. Siempre configura --days=N o --keep=N en el schedule semanal.

5. Versionar y auditar exactamente los mismos fields sin coordinación ​

Duplicación pura: si cada save genera tanto una Version como un AuditEvent con la misma info, estás pagando 2× el storage por la misma información. Coordina: versioning para contenido, audit para intención/contexto (razón, IP, user agent, aprobación).

Cuándo usar ambos ​

Los dominios regulados (legal-tech, fintech, healthtech, gobierno) combinan los dos:

  • Versioning preserva contenido inmutable (requerido para compliance — el contrato tal como estaba el 2024-03-15).
  • Audit preserva contexto humano (quién aprobó, IP de origen, razón declarada).

Mira legal-contracts.md para la implementación.

Cuándo no usar ninguno ​

  • Registros read-only o inmutables por diseño (e.g., Currency, Country).
  • Tablas de cache/lookup que pueden ser regeneradas.
  • Datos de sesión / temporales.
  • Métricas y telemetría — usa el pipeline de observabilidad dedicado.

Relacionado ​

Licencia MIT — construido con Inertia + React + Laravel.