Концепции

На этой странице описаны ключевые понятия Letopis. Прочитайте её перед изучением API.


Коллекции и сущности

Коллекция — именованная группа сущностей одного типа, например crm.deals, docs.contracts или payments.invoices. Имена коллекций — строчные, разделённые точкой идентификаторы (^[a-z0-9]+(?:\.[a-z0-9]+)*$). Letopis не интерпретирует смысл имени — это просто пространство имён.

Сущность — отдельный отслеживаемый объект внутри коллекции, идентифицируемый строкой entity_id, уникальной в рамках коллекции.

Коллекции создаются автоматически при первой записи, если collections.auto_create: true (по умолчанию). Отключите это, чтобы требовать явного PUT /collections/{c}/config перед записью.


События и диффы

Каждое изменение сущности записывается как событие. Событие содержит:

  • Дифф — список изменений на уровне полей (add, change, remove) с путём, старым и новым значением.
  • Версию — монотонно растущее целое число, per-entity.
  • Метаданные: author_id, source, ts_source, ts_received, ts_stored и произвольный объект meta.

Ingest по полному состоянию vs по диффу

  • Полное состояние (POST /state): отправляется весь текущий объект; Letopis сам вычисляет дифф.
  • Готовый дифф (POST /diff): отправляется список изменений; Letopis валидирует формат и сохраняет как есть.

Оба варианта создают идентичные события в хранилище.


Режимы надёжности

Режим Ответ Гарантия Применение
strict 201 Created Записано в MongoDB до ответа Синхронное подтверждение, финансовые записи
durable 202 Accepted + тикет Помещено в Redis Streams; воркер пишет в MongoDB По умолчанию; переживает перезапуск API
fast 202 Accepted + тикет In-memory очередь Максимальная пропускная способность

Тикеты: Асинхронные режимы возвращают ticket_id. Запрашивайте GET /tickets/{id} для отслеживания статуса: accepted → processing → stored. Тикеты истекают через конфигурируемый TTL (по умолчанию 24 ч).


Слепки и point-in-time реконструкция

Letopis хранит каждое событие append-only. Слепки решают проблему медленного чтения для длинных историй.

Каждые N событий (по умолчанию 100) Letopis материализует текущее состояние как слепок. Point-in-time чтение воспроизводит только хвост событий от ближайшего слепка. p99 менее 7 мс для историй из 10 000 версий.


Хэш-цепи

Плагин hash_chain добавляет защиту от подделки:

hash₁ = sha256("letopis:genesis:v1:" + collection_name)
hash₂ = sha256(hash₁ ‖ canonical(event₁))
hash₃ = sha256(hash₂ ‖ canonical(event₂))

Каждое событие хранит hash и prev_hash. Удаление, вставка или изменение любого события нарушает все последующие хэши. Эндпоинт :verify обнаруживает первое расхождение.

GDPR-удаление (purge) удаляет всю цепь сущности и записывает надгробную запись; другие сущности не затрагиваются.


Мультитенантность

Каждый тенант владеет изолированной базой данных MongoDB. Коллекции, события, слепки, правила и тикеты никогда не смешиваются между тенантами. Тенант идентифицируется по API-ключу; ID тенанта не фигурирует в URL.

Физическая коллекция Содержимое
ev_{name} Append-only события
sn_{name} Слепки
cur_{name} Материализованное текущее состояние

Бизнес-потоки

Поток связывает события через коллекции в причинно-следственный DAG. Каждое событие может содержать блок flow с flow_id, именем шага step и списком caused_by. Letopis сохраняет эти связи и предоставляет их через Read API.


Плагины

Хук Когда запускается
pre-store После валидации, до записи — может отклонить или обогатить событие
post-store После дурабельной записи — для side-effect логики
action Запускается через Admin API

Публичный Go API — pkg/ext, semver-стабилен. Внутренние пакеты не стабилизированы.