Adaptadores de base de datos

Comprende el contrato de persistencia colaborativa y elige entre Memory, SQLite, PostgreSQL y Database Adapters personalizados.

Un Database Adapter persiste los datos colaborativos principales que utiliza Collaboration Service. Puedes implementar IDatabaseAdapter para conectar cualquier base de datos que satisfaga su contrato de comportamiento completo. Service gestiona la semántica de Unit, OT y revisiones; Adapter la convierte en operaciones de persistencia atómicas y seguras ante reintentos.

Los Database Adapters son de código abierto bajo Apache-2.0; el código está en univer-collaboration-sdk-community, con los mismos nombres de paquete y versiones que el resto de paquetes.

Verificar la persistencia

Detén Quick Start y ejecuta pnpm example:database-adapter desde la raíz. Abre http://127.0.0.1:3010/?unit=persistent-sheet, modifica una celda y compruébala en otro navegador. Detén el servidor, repite el comando y abre la misma URL: el valor debe permanecer. El archivo es examples/database-adapter/.data/collaboration.sqlite; no lo borres entre ejecuciones. El servidor lee el Unit existente y llama a createUnitFromData() solo ante UNIT_NOT_FOUND. Consulta server/main.ts.

Qué almacena un Adapter

El Adapter principal almacena:

  • el registro Unit y la revisión actual de cabecera;
  • instantáneas con revisión;
  • conjuntos de cambios confirmados;
  • bloques referenciados por las instantáneas Sheet;
  • registros de idempotencia1 de envío con clave (unitID, sid, reqId);
  • estado de eliminación lógica, recuperación y eliminación permanente de Units.

No almacena por defecto usuarios, roles, ACL ni metadatos del producto. History, Thread Comment y Worktree tienen contratos Adapter independientes.

Semántica obligatoria

IDatabaseAdapter es más que una interfaz CRUD. La implementación debe mantener las garantías de corrección que espera Collaboration Service:

ContratoSignificado
Creación atómicaLa instantánea inicial y los bloques Sheet se hacen visibles juntos o no se hace visible ninguno
CAS2 de revisiónEl siguiente envío solo se confirma cuando la revisión de cabecera coincide con la esperada
Idempotencia de envíoReintentar el mismo (unitID, sid, reqId) devuelve el mismo resultado determinado
Cambios contiguosLos conjuntos de cambios confirmados y la revisión de la Unit mantienen continuidad
Visibilidad de instantáneasUna instantánea solo se puede leer tras almacenar todas sus dependencias
Ciclo de vida atómicoTodas las Units del lote de eliminación o recuperación tienen éxito, o ninguna
Marca de eliminación permanenteNo se puede volver a crear un ID de Unit eliminado permanentemente

Convertir cada método en una lectura o escritura normal sin implementar esta concurrencia y atomicidad rompe OT y la recuperación.

Memory Adapter

@univerjs-pro/collaboration-database-memory es una implementación dentro del proceso apropiada para:

  • pruebas unitarias y de integración;
  • entornos temporales de desarrollo;
  • validación rápida de Middleware personalizados o del montaje del cliente.
TypeScript
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 sigue el mismo contrato de revisión, CAS e idempotencia que las implementaciones persistentes, pero los datos viven en el proceso Node.js actual y se pierden cuando termina.

SQLite Adapter

@univerjs-pro/collaboration-database-sqlite es una implementación persistente SQLite para desarrollo, pruebas, aplicaciones locales y uso a pequeña escala.

TypeScript
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 });

Inicializa el esquema en una base vacía y solo verifica una existente. La ubicación del archivo, las copias de seguridad y otros ajustes SQLite los decide la aplicación.

PostgreSQL Adapter

@univerjs-pro/collaboration-database-postgres es una implementación persistente basada en node-postgres que varios nodos de colaboración pueden compartir.

TypeScript
import { PostgresDatabaseAdapter } from "@univerjs-pro/collaboration-database-postgres";import { UniverCollabService } from "@univerjs-pro/collaboration-service";import { Pool } from "pg";const pool = new Pool({ connectionString: process.env.DATABASE_URL });const database = await PostgresDatabaseAdapter.create({ pool });const service = new UniverCollabService({ dbAdapter: database });

La aplicación es dueña del pool y lo cierra después de liberar el Service. create() inicializa el esquema collaboration en una base vacía y solo verifica uno existente.

Database Adapters personalizados

Un Adapter personalizado suele ser adecuado cuando:

  • la aplicación ya utiliza MySQL u otra base de negocio;
  • los envíos colaborativos deben integrarse con mecanismos de base específicos de la aplicación;
  • las necesidades operativas o de cumplimiento exceden el alcance de los Adapters integrados.

Implementa IDatabaseAdapter e inyéctalo en Service igual que un Adapter integrado:

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

Service no depende de una base concreta, pero tampoco puede añadir transacciones, CAS o deduplicación a una implementación personalizada. Adapter debe implementar el contrato completo con las capacidades de su base de datos. Las implementaciones integradas del repositorio de la comunidad muestran el mismo contrato en distintas bases de datos y sirven de referencia.

Context y límite de la aplicación

Cada llamada a Service pasa su customData a Middleware, Database Adapter y Events relacionados, pero el SDK no lo persiste automáticamente. Las decisiones de autorización suelen mantenerse en el Middleware de Service en vez de repetirse en los métodos del Adapter:

text
Middleware de Service  → permite o rechaza la operación de negocio  → Database Adapter  → almacena atómicamente el estado colaborativo autoritativo

Así, Memory, SQLite, PostgreSQL y los Adapters personalizados comparten la misma política de aplicación.

Si los datos de negocio requieren consistencia fuerte con los colaborativos, un Database Adapter personalizado puede leer valores de customData y escribir ambos conjuntos dentro de su transacción de base de datos.

Si no necesitan compartir la transacción colaborativa, Middleware o Events pueden escribirlos en otras tablas o sistemas externos. No comparten por defecto la transacción interna del Adapter. Los Events son notificaciones locales al proceso y de mejor esfuerzo emitidas tras un commit colaborativo correcto. La aplicación es responsable de la idempotencia, los reintentos y las compensaciones necesarios.

Almacenamiento de extensiones

History, Thread Comment, Worktree y File definen interfaces Database Adapter independientes. Sus Adapters pueden compartir una base o usar almacenamiento separado. Una implementación personalizada también puede proporcionar varias interfaces Adapter e inyectarse en los Services correspondientes.

Como son interfaces independientes, el SDK no proporciona transacciones entre módulos por defecto. Aunque compartan base de datos, la aplicación define la estrategia de consistencia entre módulos.

Elegir un Adapter

AdapterUso más adecuadoRestricción principal
MemoryPruebas, ejemplos, desarrollo temporalLos datos desaparecen al terminar el proceso
SQLiteDesarrollo, pruebas, aplicaciones locales, pequeña escalaBase de un archivo con ajustes a cargo de la aplicación
PostgreSQLDespliegues en varios nodos y bases PostgreSQL existentesLa aplicación gestiona el pool y la operación de la base
PersonalizadoBases existentes o integración de datos de negocioLa aplicación debe implementar y verificar el contrato completo

La aplicación es propietaria de cada Adapter inyectado. Libera Service antes de Adapter:

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

Consulta el ejemplo de Database Adapter, ejecutable y con implementaciones para comparar.

Footnotes

  1. La idempotencia permite reintentar un envío sin aplicar sus cambios dos veces. Conserve el identificador original; el mismo contenido con otro identificador constituye un nuevo envío. ↩

  2. CAS actualiza un valor solo si aún coincide con el esperado. La comprobación de la revisión y la escritura siguiente deben ser atómicas para impedir que dos envíos concurrentes confirmen la misma revisión. ↩

¿Cómo es esta guía?

© 2026 DreamNum Co., Ltd.