ADR-008: Аудит-лог изменений каталога и прейскурантов

Статус

accepted

Контекст

Каталог и прейскуранты — мутабельные сущности без версионности. Франшиза может в любой момент изменить цену, название, категорию товара. Заказы защищены снапшотами (цена/название копируются в строку заказа при создании). Но аналитика и отчёты работают с текущим состоянием каталога.

Проблема

При изменении каталога теряется информация о том, что было раньше:

Что изменилосьПроблема для аналитики
Цена 690₽ → 750₽Нельзя узнать когда именно поменялась цена. Отчёт за период не может разделить продажи по старой и новой цене
Название “Пицца Маргарита” → “Маргарита Классическая”В отчёте по товару — два визуально разных названия для одного product_id
Категория “Пицца” → “Итальянская кухня”Отчёт “Продажи по категориям” за прошлый месяц — товар “переезжает” в новую категорию, искажая данные
Тип dish → goodВ Phase 2 влияет на складское списание. Историческая аналитика по типу товара искажается

Почему не версионность

Полная версионность каталога (снапшоты каждого изменения, откат) — избыточна:

  • Заказы уже защищены снапшотами
  • Меню франчайзи версионируется отдельно (BR 1.11)
  • Версионность каталога добавляет сложность без явной бизнес-потребности

Нужен не откат, а журнал: когда, кто, что поменял.

Решение

Аудит-лог изменений — лёгкая таблица catalog_changelog:

CREATE TABLE catalog_changelog (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    entity_type VARCHAR(50) NOT NULL,   -- 'product', 'category', 'price_list_item', 'modifier'
    entity_id UUID NOT NULL,
    field VARCHAR(100) NOT NULL,         -- 'price', 'name', 'category_id', 'status'
    old_value TEXT,
    new_value TEXT,
    changed_by UUID NOT NULL,            -- user_id
    changed_at TIMESTAMP NOT NULL DEFAULT now()
);

Примеры записей:

entity_type: product, entity_id: uuid, field: price, old: 690, new: 750, changed_by: user, changed_at: 2026-04-15
entity_type: product, entity_id: uuid, field: name, old: Пицца Маргарита, new: Маргарита Классическая
entity_type: price_list_item, entity_id: uuid, field: price, old: 690, new: 750

Когда реализовать

На MVP — не реализуем. Заказы защищены, аналитики ещё нет. Аудит-лог добавляется при появлении Report Service (Phase 2).

Как реализовать (Phase 2)

  • Middleware / interceptor в Catalog Service: при каждом UPDATE записывает diff в catalog_changelog
  • Не блокирует основную операцию (async или after-commit)
  • Report Service читает changelog для корректной привязки данных к периодам

Последствия

Положительные

  • Аналитика может строить отчёты с учётом исторических изменений
  • Нет рефакторинга — одна таблица + middleware, добавляется аддитивно
  • Не усложняет MVP

Отрицательные

  • До Phase 2 аналитика работает с текущим состоянием (некорректные отчёты по категориям/ценам при изменениях)

Риски

  • Если отчёты понадобятся раньше Phase 2 — changelog придётся добавить раньше

Ссылки