# Адаптеры баз данных

> Изучите контракт хранения данных совместной работы и выберите Memory, SQLite или собственный Database Adapter.

- Human documentation: [https://docs.univer.ai/ru-RU/server/collaboration/database-adapters](https://docs.univer.ai/ru-RU/server/collaboration/database-adapters)

- Agent Markdown: [https://docs.univer.ai/ru-RU/server/collaboration/database-adapters.md](https://docs.univer.ai/ru-RU/server/collaboration/database-adapters.md)

- Requested language: `ru-RU`

- Content language: `ru-RU`

- Documentation version: `1.2.0`

- Source: [collaboration/database-adapters.ru-RU.mdx](https://github.com/dream-num/documentation/blob/dev/content/server/collaboration/database-adapters.ru-RU.mdx)

---

Database Adapter сохраняет основные данные совместной работы, используемые Collaboration Service. Разработчик может реализовать `IDatabaseAdapter` для любой базы, способной выполнить полный контракт поведения. Service отвечает за семантику Unit, OT и ревизий. Adapter превращает её в атомарные операции хранения, безопасные при повторе.

## Проверка сохранения после перезапуска

Остановите Quick Start и выполните `pnpm example:database-adapter` из корня. Откройте `http://127.0.0.1:3010/?unit=persistent-sheet`, измените ячейку и проверьте её в другом браузере. Остановите сервер, повторите команду и откройте тот же URL: значение должно сохраниться. Файл — `examples/database-adapter/.data/collaboration.sqlite`; не удаляйте его между запусками. Сервер читает существующий Unit и вызывает `createUnitFromData()` только при `UNIT_NOT_FOUND`. См. [server/main.ts](https://github.com/dream-num/univer-collaboration-examples/blob/main/examples/database-adapter/server/main.ts).

## Что хранит Adapter

Основной Adapter совместной работы хранит:

* запись Unit и текущую головную ревизию;
* снимки с ревизиями;
* подтверждённые наборы изменений;
* блоки, на которые ссылаются снимки Sheet;
* записи идемпотентности[^submission-idempotency] отправок с ключом `(unitID, sid, reqId)`;
* состояние логического удаления, восстановления и окончательного удаления Unit.

По умолчанию он не хранит пользователей, роли, ACL, каталоги, файлы и метаданные продукта. History, Thread Comment и Worktree используют собственные независимые контракты Adapter.

## Обязательная семантика

`IDatabaseAdapter` — не просто CRUD-интерфейс. Реализация должна сохранять гарантии корректности, ожидаемые Collaboration Service:

| Контракт                      | Значение                                                                           |
| ----------------------------- | ---------------------------------------------------------------------------------- |
| Атомарное создание            | Начальный снимок и блоки Sheet становятся видимыми вместе либо не становятся вовсе |
| CAS[^revision-cas] ревизии    | Следующая отправка подтверждается лишь при совпадении головной ревизии с ожидаемой |
| Идемпотентность отправки      | Повтор одного `(unitID, sid, reqId)` возвращает тот же определённый результат      |
| Непрерывные изменения         | Подтверждённые наборы изменений и ревизия Unit остаются непрерывными               |
| Видимость снимков             | Снимок доступен для чтения лишь после сохранения всех зависимостей                 |
| Атомарный жизненный цикл      | Все Units пакета удаления или восстановления обрабатываются успешно либо ни одна   |
| Метка окончательного удаления | Окончательно удалённый ID Unit нельзя создать заново                               |

Простое сопоставление методов обычным операциям чтения и записи без этой семантики конкурентности и атомарности нарушит OT и восстановление.

## Memory Adapter

`@univerjs-pro/collaboration-database-memory` — реализация в памяти процесса для:

* модульных и интеграционных тестов;
* официальных примеров;
* временных сред разработки;
* быстрой проверки собственных Middleware или сборки клиента.

```ts
import { MemoryDatabaseAdapter } from "@univerjs-pro/collaboration-database-memory";
import { UniverCollabService } from "@univerjs-pro/collaboration-service";

const database = new MemoryDatabaseAdapter();
const service = new UniverCollabService({ dbAdapter: database });
```

Memory Adapter соблюдает те же контракты ревизий, CAS и идемпотентности, что и постоянные реализации, но все данные находятся в текущем процессе Node.js и теряются при его завершении.

## SQLite Adapter

`@univerjs-pro/collaboration-database-sqlite` — постоянное хранилище SQLite для разработки, тестов, локальных приложений и небольших нагрузок.

```ts
import { mkdir } from "node:fs/promises";

import { SQLiteDatabaseAdapter } from "@univerjs-pro/collaboration-database-sqlite";
import { UniverCollabService } from "@univerjs-pro/collaboration-service";

await mkdir("./data", { recursive: true });

const database = new SQLiteDatabaseAdapter({
  filename: "./data/collaboration.sqlite",
  busyTimeoutMs: 5_000,
});
const service = new UniverCollabService({ dbAdapter: database });
```

SQLite Adapter использует внешние ключи и транзакции записи `BEGIN IMMEDIATE` для атомарных коммитов. В пустой базе он создаёт актуальную схему. Неполные или неподдерживаемые схемы он отказывается открывать, а не применяет неизвестные миграции автоматически.

Он не меняет `journal_mode` и не определяет расположение файла, политику резервного копирования и другие настройки SQLite. SQLite предназначен для разработки, локальных приложений и небольших масштабов.

## Собственные Database Adapters

Собственный Adapter обычно подходит, когда:

* приложение уже использует PostgreSQL, MySQL или другую бизнес-базу;
* отправки совместной работы должны интегрироваться со специфическими механизмами базы приложения;
* эксплуатационные или нормативные требования выходят за предполагаемый масштаб SQLite.

Реализуйте `IDatabaseAdapter` и внедрите его в Service так же, как встроенный Adapter:

```ts
const database = new ApplicationCollaborationDatabase({ pool });
const service = new UniverCollabService({ dbAdapter: database });
```

Service не зависит от конкретной базы, но и не может добавить транзакции, CAS или дедупликацию в вашу реализацию. Adapter должен выполнить полный контракт средствами своей базы.

## Context и граница приложения

Каждый вызов Service передаёт `customData` в Middleware, Database Adapter и связанные Events, но SDK не сохраняет его автоматически. Решения об авторизации обычно остаются в Middleware Service, а не повторяются в методах Adapter:

```text
Middleware Service
  → разрешает или отклоняет бизнес-операцию
  → Database Adapter
  → атомарно сохраняет эталонное состояние совместной работы
```

Так Memory, SQLite и собственные Adapters используют общие политики приложения.

Если бизнес-данным нужна строгая согласованность с данными совместной работы, собственный Database Adapter может прочитать значения из `customData` и записать оба набора в своей транзакции базы данных.

Если общая транзакция не нужна, Middleware или Events могут записать бизнес-данные в дополнительные таблицы или внешние системы. По умолчанию они не разделяют внутреннюю транзакцию Adapter. Events — локальные для процесса уведомления с доставкой по возможности, испускаемые после успешного коммита. Необходимую идемпотентность, повторы и компенсации реализует приложение.

## Хранение расширений

History, Thread Comment и Worktree определяют независимые интерфейсы Database Adapter. Их Adapters могут использовать одну базу или разные хранилища. Одна собственная реализация также может предоставлять несколько интерфейсов Adapter и внедряться в соответствующие Services.

Из-за независимости интерфейсов SDK по умолчанию не предоставляет межмодульных транзакций. Даже при общей базе стратегию межмодульной согласованности определяет приложение.

## Выбор Adapter

| Adapter     | Подходящие задачи                                          | Основное ограничение                                     |
| ----------- | ---------------------------------------------------------- | -------------------------------------------------------- |
| Memory      | Тесты, примеры, временная разработка                       | Данные исчезают при завершении процесса                  |
| SQLite      | Разработка, тесты, локальные приложения, небольшой масштаб | Однофайловая база с настройками под контролем приложения |
| Собственный | Существующие базы или особая интеграция бизнес-данных      | Приложение реализует и проверяет полный контракт         |

Приложение владеет всеми внедрёнными Adapters. Освободите Service раньше Adapter:

```ts
await service.dispose();
await database.dispose();
```

Сравнить работающие реализации можно в [примере Database Adapter](https://docs.univer.ai/ru-RU/server/collaboration/examples.md#database-adapter).

[^submission-idempotency]: Идемпотентность позволяет повторить отправку без повторного применения изменений. Сохраняйте идентификатор отправки: те же данные с новым идентификатором считаются новой отправкой.

[^revision-cas]: CAS обновляет значение, только если оно совпадает с ожидаемым. Проверка текущей ревизии и запись следующего состояния должны быть атомарными, чтобы два параллельных запроса не подтвердили одну ревизию.
