Концепция версий в Dexie

Роль версий в управлении структурой IndexedDB

Dexie.js строится поверх IndexedDB и наследует его ключевую особенность — обязательное версионирование схемы базы данных. Любое изменение структуры хранилищ, индексов или первичных ключей должно сопровождаться увеличением версии базы. Эта модель исключает произвольную модификацию схемы «на лету» и переводит все изменения в контролируемые миграции.

Версия базы данных в Dexie.js представляет собой числовой идентификатор состояния схемы. Каждое увеличение версии сигнализирует движку IndexedDB о необходимости запуска процедуры обновления структуры, включающей создание, модификацию или удаление объектных хранилищ (object stores) и индексов.

Объявление версии через API Dexie

В Dexie.js версия задаётся через метод version() у экземпляра базы данных. Каждая версия описывает состояние схемы через строку определения таблиц и их индексов.

const db = new Dexie("AppDatabase");

db.version(1).stores({
  users: "++id, name, email"
});

Первая версия фиксирует начальную структуру. Строка схемы определяет:

  • users — имя таблицы
  • ++id — автоинкрементный первичный ключ
  • name, email — индексируемые поля

Любое последующее изменение структуры требует новой версии.

Принцип неизменяемости версий

После публикации версии её описание становится неизменяемым. Dexie.js рассматривает версии как исторические слепки схемы. Изменение ранее объявленной версии не допускается, поскольку IndexedDB опирается на строгую последовательность миграций.

Добавление новой таблицы или индекса осуществляется только через новую версию:

db.version(2).stores({
  users: "++id, name, email, age",
  orders: "++id, userId, total"
});

В этом примере расширена таблица users и добавлена новая таблица orders. Dexie интерпретирует это как переход от состояния версии 1 к версии 2.

Механизм миграции между версиями

При открытии базы данных Dexie сравнивает текущую версию хранилища в браузере с последней объявленной версией в коде. Если обнаруживается несоответствие, запускается процесс миграции.

Миграция проходит последовательно через все промежуточные версии, если их несколько. Например, при переходе с версии 1 на версию 3 будут выполнены шаги 1 → 2 → 3.

Каждый переход может сопровождаться функцией upgrade, содержащей пользовательскую логику преобразования данных.

db.version(2).stores({
  users: "++id, name, email, age"
}).upgrade(tx => {
  return tx.table("users").toCollection().modify(user => {
    user.age = 0;
  });
});

Здесь добавление поля age сопровождается миграцией существующих записей.

Структура определения версии

Определение версии включает два ключевых элемента:

  • схема таблиц через stores()
  • опциональная функция миграции через upgrade()

Эти элементы связаны с конкретным номером версии и формируют атомарное описание состояния базы.

db.version(3)
  .stores({
    users: "++id, name, email, age, createdAt"
  })
  .upgrade(tx => {
    return tx.table("users").toCollection().modify(user => {
      user.createdAt = Date.now();
    });
  });

Правила совместимости схем

Dexie.js накладывает строгие ограничения на изменение схемы:

  • удаление индекса требует пересоздания таблицы через новую версию
  • изменение первичного ключа невозможно без полной миграции данных
  • добавление новых полей не требует миграции, но требует обновления версии
  • изменение структуры индексов фиксируется только через новую версию

Каждое изменение трактуется как переход состояния, а не как патч существующей схемы.

Логика разрешения конфликтов версий

Если код приложения содержит несколько объявлений одной и той же версии, применяется первое объявление в цепочке и фиксируется как финальное. Повторное определение версии игнорируется, поскольку Dexie формирует внутренний список версий при инициализации экземпляра базы данных.

При попытке уменьшения версии возникает ошибка, так как IndexedDB не поддерживает откат схемы.

Последовательность регистрации версий

Версии должны объявляться в возрастающем порядке. Dexie формирует цепочку миграций, упорядоченную по числовому значению версии:

db.version(1).stores({ users: "++id, name" });

db.version(2).stores({ users: "++id, name, email" });

db.version(3).stores({
  users: "++id, name, email, age",
  logs: "++id, type, createdAt"
});

Каждая новая версия расширяет или изменяет предыдущее состояние, но не заменяет его.

Поведение при открытии базы данных

При вызове db.open() происходит анализ состояния:

  • проверка текущей версии IndexedDB
  • сравнение с максимальной версией Dexie
  • запуск миграций при необходимости
  • блокировка операций до завершения обновления схемы

В процессе обновления база может быть временно недоступна для операций чтения и записи, поскольку структура находится в переходном состоянии.

Работа с данными при обновлении версии

Во время миграции доступ к данным осуществляется через транзакции, привязанные к конкретной версии. Dexie обеспечивает консистентность данных, гарантируя, что изменения схемы и модификации записей выполняются атомарно в рамках одной миграционной операции.

Пример массового преобразования данных:

db.version(4).stores({
  users: "++id, name, email, isActive"
}).upgrade(async tx => {
  const users = tx.table("users");
  await users.toCollection().modify(user => {
    user.isActive = true;
  });
});

Ограничения модели версий

Версионная система Dexie.js подчиняется ограничениям IndexedDB:

  • отсутствует механизм частичного обновления схемы без увеличения версии
  • невозможна параллельная миграция нескольких несовместимых схем
  • каждое изменение требует полной последовательности переходов
  • операции миграции блокируют доступ к базе до завершения процесса

Эти ограничения формируют детерминированную модель управления структурой данных.

Внутреннее представление версий

Dexie хранит список версий как упорядоченный набор описаний схем. Каждая запись содержит:

  • номер версии
  • описание таблиц и индексов
  • функцию миграции (если определена)
  • метаданные состояния

При инициализации формируется граф переходов между версиями, где каждая вершина представляет состояние схемы, а ребро — операцию миграции.

Роль версий в эволюции приложения

Версионная модель позволяет управлять изменениями структуры данных в долгоживущих приложениях. Каждое изменение схемы становится явным шагом эволюции, фиксируемым в коде. Это исключает рассинхронизацию между клиентскими экземплярами и гарантирует предсказуемость состояния базы при обновлениях приложения.