Границы и связи модулей

Изучите роли, потоки данных и сборку Collaboration Client, Transport, Endpoint, Service и Database Adapter.

Univer Collaboration объединяет браузерный Client с серверными модулями. Каждый модуль отвечает за одну категорию задач. Приложение собирает их через публичные интерфейсы, не вмешиваясь во внутреннюю реализацию OT1, протокола или хранения.

Полная схема модулей

100%

Collaboration Client

Браузерный Collaboration Client работает рядом с Web SDK и:

  • запрашивает снимки Unit и недостающие подтверждённые изменения;
  • получает одноразовый Session Ticket и открывает WebSocket;
  • входит в Rooms Unit и обменивается Presence;
  • отправляет локальные мутации наборами изменений;
  • получает ACK и рассылки, восполняет пропуски после переподключения.

Client не решает, доверять ли пользователю, и не является источником истины. Сервер по-прежнему обеспечивает идентификацию и авторизацию через подходящие Middleware.

Node Transport

@univerjs-pro/collaboration-transport-node — вход HTTP/WebSocket для Node.js. Он принимает запросы и обновления до WebSocket от HTTP-сервера приложения, выполняет входные Middleware и направляет трафик в Endpoints.

Transport не знает OT, данных Unit или моделей ACL. Основные методы сборки:

TypeScript
transport.use(httpMiddleware);transport.useUpgrade(upgradeMiddleware);transport.register(endpoint);
  • use() участвует в обычном HTTP, может задать userID, customData, логи и трассировку;
  • useUpgrade() участвует в рукопожатии WebSocket и может отказать до подключения;
  • register() регистрирует Endpoint протокола и передаёт его жизненный цикл Transport.

Collaboration Endpoint

@univerjs-pro/collaboration-endpoint реализует протокол Univer Collaboration Client и отвечает за:

ВозможностьОтветственность Endpoint
Загрузка содержимогоПередаёт Client снимки Unit, блоки и недостающие подтверждённые изменения
Отправка измененийПринимает изменения, возвращает ACK, рассылает подтверждённые изменения в Room
Подключение SessionВыдаёт одноразовые билеты, создаёт и поддерживает Sessions WebSocket
Совместная работа RoomОбрабатывает JOIN, LEAVE, состояние участников и Presence
Жизненный цикл UnitПреобразует запросы удаления и восстановления в вызовы Service
Несколько узловПередаёт Room, Presence и рассылки между узлами через необязательный pub/sub-адаптер и общее хранилище билетов

Endpoint не выполняет OT и не хранит данные напрямую. Он получает эталонные результаты от Service и отображает их в протокол Client. Операции продукта, например создание документа, должны вызывать Service из API приложения.

Collaboration Service

@univerjs-pro/collaboration-service — независимое от сети ядро для Sheet, Doc, Slide, Board и Base. Оно:

  • читает данные восстановления Unit и подтверждённые изменения;
  • создаёт Units из Unit Data или снимков;
  • отправляет изменения, выполняет OT и продвигает непрерывные ревизии2;
  • распознаёт дубликаты по (unitID, sid, reqId);
  • логически удаляет, восстанавливает и окончательно удаляет Units;
  • выполняет Middleware Service и испускает Events после подтверждения.

Service не предоставляет HTTP, WebSocket, пользователей, роли, ACL, иерархию или управление файлами. Endpoint использует его для протокола браузера. Приложение также может вызывать публичный API из бизнес-API или фоновых задач.

Основные API

ВозможностьAPI Service
Загрузить UnitgetUnitLoadData(), getUnitLoadDataWithBlocks()
Прочитать приращенияgetChangesets(), getSheetBlock()
Создать UnitcreateUnitFromData(), createUnitFromSnapshot()
Отправить измененияsubmitChangeset()
Удалить и восстановитьdeleteUnits(), recoverUnits()

Database Adapter

Database Adapter — контракт между Service и хранилищем. Он гарантирует:

  • атомарное создание начального снимка и зависимостей;
  • сравнение и обмен ревизии;
  • идемпотентность отправки изменений;
  • видимость снимков только после появления всех зависимостей;
  • пакетные операции жизненного цикла Unit по принципу «всё или ничего».

SDK предоставляет Memory и SQLite. Для другой базы реализуйте тот же контракт. См. Адаптеры баз данных.

Четыре основных понятия

ПонятиеЗначение
UnitНезависимо загружаемое и совместно редактируемое содержимое Univer с уникальным unitID и типом
SnapshotСохранённое содержимое Unit на ревизии, а не изменяемый живой объект
ChangesetОтправленный набор мутаций с базовой ревизией и идентификатором идемпотентности
RevisionНепрерывный номер версии подтверждённого состояния Unit, изначально 1

Работающее содержимое изменяют через Facade API или Commands. Изменение объекта снимка не обновляет живую Unit в Client или Service.

Загрузка Unit

100%

userID/customData, заданные Middleware Transport, проходят с текущим HTTP-запросом в Middleware Service. Политика приложения решает, разрешено ли чтение; для прямого HTTP-чтения вход в Room не обязателен.

Отправка правки

text
Facade / Command  → Mutation  → Changeset клиента  → отправка в Endpoint  → Middleware Service  → OT + CAS ревизии  → атомарный коммит Adapter  → подтверждённый Changeset  → ACK / рассылка Room

Подтверждённая ревизия Database Adapter является эталоном. ACK и рассылки синхронизируют клиентов в сети с малой задержкой. Пропустив сообщение, клиент может получить подтверждённые изменения от известной ревизии.

Минимальная сборка сервера

TypeScript
import { createServer } from "node:http";import { MemoryDatabaseAdapter } from "@univerjs-pro/collaboration-database-memory";import { UniverCollabEndpoint } from "@univerjs-pro/collaboration-endpoint";import { UniverCollabService } from "@univerjs-pro/collaboration-service";import { createNodeTransport } from "@univerjs-pro/collaboration-transport-node";const database = new MemoryDatabaseAdapter();const service = new UniverCollabService({ dbAdapter: database });const endpoint = new UniverCollabEndpoint(service);const transport = createNodeTransport();transport.use(async (context, next) => {  context.userID = "demo-user";  await next();});transport.register(endpoint);const server = createServer();transport.attach(server);server.listen(3010);

Фиксированный пользователь и Memory Adapter только демонстрируют сборку. Приложение устанавливает реальный Context через Middleware и выбирает Adapter по требованиям к данным.

Создание Unit через API приложения

Приложение может определить свой HTTP/RPC endpoint и вызвать созданный service после аутентификации, проверки входа и бизнес-правил:

text
Бизнес-клиент  → POST /api/units  → аутентификация и проверка приложения  → service.createUnitFromData()  → Database Adapter

Пример Express предполагает, что Middleware аутентификации задал response.locals.user, а request.body.data уже проверен как Workbook Data:

TypeScript
import { randomUUID } from "node:crypto";import { json } from "express";import { UniverType } from "@univerjs/protocol";app.post("/api/units", json({ limit: "1mb" }), async (request, response, next) => {  try {    const user = response.locals.user as { readonly userID: string };    const unitID = randomUUID();    const result = await service.createUnitFromData(      {        type: UniverType.UNIVER_SHEET,        data: {          ...request.body.data,          id: unitID,          rev: 1,        },      },      {        userID: user.userID,        customData: { traceID: randomUUID() },      },    );    response.status(result.status === "created" ? 201 : 200).json(result);  } catch (error) {    next(error);  }});

POST /api/units принадлежит приложению, а не протоколу Collaboration Client. createUnitFromData() всё равно выполняет Middleware createUnit Service. Записи продукта, ACL владельца, преобразование ошибок в ответы и компенсация сбоев остаются за приложением.

Освобождение ресурсов

transport.dispose() освобождает Endpoints, зарегистрированные через register(). Приложение отдельно освобождает Service и внедрённый Database Adapter в обратном порядке сборки:

TypeScript
await transport.dispose();await service.dispose();await database.dispose();

Если вы передали pub/sub-адаптер, освободите его и его соединения после transport; Endpoint этого не делает.

Продолжите с Middleware и Events, чтобы расширять поведение на этих границах.

Footnotes

  1. Операционная трансформация (OT) согласует одновременные операции, созданные на разных ревизиях документа. Она обрабатывает изменения, а не заменяет весь документ последним полученным снимком. ↩

  2. Ревизия обозначает позицию в подтверждённой истории Unit. Changeset содержит операции одной отправки; их подтверждение продвигает историю. Ревизия не является версией SDK или меткой времени. ↩

Насколько полезно это руководство?

© 2026 DreamNum Co., Ltd.