Adaptateurs de base de données

Comprenez le contrat de persistance collaborative et choisissez entre Memory, SQLite, PostgreSQL et un Database Adapter personnalisé.

Un Database Adapter persiste les données collaboratives centrales utilisées par Collaboration Service. Les développeurs peuvent implémenter IDatabaseAdapter pour connecter toute base capable de satisfaire son contrat comportemental complet. Le Service gère la sémantique des Units, de l’OT et des révisions ; l’Adapter la traduit en opérations de persistance atomiques et sûres à réessayer.

Les Database Adapters sont open source sous Apache-2.0 ; le code se trouve dans univer-collaboration-sdk-community, avec les mêmes noms de paquet et versions que les autres paquets.

Vérifier la persistance

Arrêtez Quick Start puis lancez pnpm example:database-adapter depuis la racine. Ouvrez http://127.0.0.1:3010/?unit=persistent-sheet, modifiez une cellule et vérifiez-la dans un second navigateur. Arrêtez, relancez la même commande et rouvrez la même URL : la valeur doit rester. Le fichier est examples/database-adapter/.data/collaboration.sqlite ; ne le supprimez pas entre les lancements. Le serveur lit le Unit existant et appelle createUnitFromData() seulement pour UNIT_NOT_FOUND. Voir server/main.ts.

Données stockées par l’Adapter

L’Adapter central stocke :

  • l’enregistrement Unit et sa révision de tête ;
  • les instantanés révisionnés ;
  • les jeux de modifications confirmés ;
  • les blocs référencés par les instantanés Sheet ;
  • les enregistrements d’idempotence1 indexés par (unitID, sid, reqId) ;
  • l’état de suppression logique, de restauration et de suppression définitive des Units.

Par défaut, il ne stocke ni utilisateurs, ni rôles, ni ACL, ni métadonnées produit. History, Thread Comment et Worktree possèdent des contrats Adapter indépendants.

Sémantique requise

IDatabaseAdapter dépasse une interface CRUD. L’implémentation doit respecter les garanties de correction attendues par Collaboration Service :

ContratSignification
Création atomiqueL’instantané initial et les blocs Sheet deviennent visibles ensemble, ou pas du tout
CAS2 de révisionLa soumission suivante est confirmée seulement si la révision de tête correspond à la révision attendue
Idempotence des soumissionsRéessayer le même (unitID, sid, reqId) renvoie le même résultat déterminé
Modifications contiguësLes jeux de modifications confirmés et la révision Unit restent contigus
Visibilité des instantanésUn instantané n’est lisible qu’après stockage de toutes ses dépendances
Cycle de vie atomiqueToutes les Units d’un lot de suppression ou de restauration réussissent, ou aucune
Marqueur de suppression définitiveL’ID d’une Unit supprimée définitivement ne peut pas être recréé

Traduire chaque méthode en simple lecture ou écriture sans ces garanties de concurrence et d’atomicité casse l’OT et la restauration.

Memory Adapter

@univerjs-pro/collaboration-database-memory est une implémentation en mémoire du processus adaptée :

  • aux tests unitaires et d’intégration ;
  • aux environnements de développement temporaires ;
  • à la validation rapide de Middleware personnalisés ou de l’assemblage client.
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 respecte les mêmes contrats de révision, de CAS et d’idempotence que les implémentations persistantes, mais toutes les données résident dans le processus Node.js courant et disparaissent à son arrêt.

SQLite Adapter

@univerjs-pro/collaboration-database-sqlite est une implémentation SQLite persistante pour le développement, les tests, les applications locales et les usages à petite échelle.

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

Il initialise le schéma dans une base vide et ne fait que vérifier une base existante. L’emplacement du fichier, les sauvegardes et les autres réglages SQLite relèvent de l’application.

PostgreSQL Adapter

@univerjs-pro/collaboration-database-postgres est une implémentation persistante basée sur node-postgres que plusieurs nœuds de collaboration peuvent partager.

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

L’application possède le pool et le ferme après avoir libéré le Service. create() initialise le schéma collaboration dans une base vide et ne fait que vérifier un schéma existant.

Database Adapters personnalisés

Un Adapter personnalisé convient généralement si :

  • l’application utilise déjà MySQL ou une autre base métier ;
  • les soumissions collaboratives doivent s’intégrer à des mécanismes de base propres à l’application ;
  • les exigences d’exploitation ou de conformité dépassent le périmètre des Adapters intégrés.

Implémentez IDatabaseAdapter et injectez-le dans le Service comme un Adapter intégré :

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

Le Service ne dépend pas d’une base particulière, mais ne peut pas ajouter transactions, CAS ou déduplication à votre implémentation. L’Adapter doit réaliser le contrat complet avec les capacités de sa base. Les implémentations intégrées du dépôt communautaire montrent le même contrat sur différentes bases et servent de référence.

Context et frontière applicative

Chaque appel Service transmet son customData aux Middleware, au Database Adapter et aux Events associés, sans persistance automatique. Les décisions d’autorisation restent généralement dans les Middleware Service plutôt que d’être répétées dans chaque méthode Adapter :

text
Middleware Service  → autorise ou refuse l’opération métier  → Database Adapter  → stocke atomiquement l’état collaboratif faisant autorité

Memory, SQLite, PostgreSQL et les Adapters personnalisés peuvent ainsi partager les mêmes politiques applicatives.

Si les données métier doivent rester fortement cohérentes avec les données collaboratives, un Database Adapter personnalisé peut lire les valeurs nécessaires dans customData et écrire les deux ensembles dans sa transaction de base de données.

Si elles n’ont pas besoin de partager cette transaction, les Middleware ou Events peuvent les écrire dans d’autres tables ou systèmes externes. Ils ne partagent pas par défaut la transaction interne de l’Adapter. Les Events sont des notifications locales au processus, fournies au mieux après la réussite du commit collaboratif. L’application assume l’idempotence, les nouvelles tentatives et les compensations nécessaires.

Stockage des modules d’extension

History, Thread Comment, Worktree et File définissent des interfaces Database Adapter indépendantes. Leurs Adapters peuvent partager une base ou utiliser des stockages distincts. Une seule implémentation personnalisée peut aussi fournir plusieurs interfaces Adapter et être injectée dans les Services correspondants.

Ces interfaces étant indépendantes, le SDK ne fournit pas par défaut de transactions intermodules. Même avec une base partagée, la stratégie de cohérence intermodules appartient à l’application.

Choisir un Adapter

AdapterUsage adaptéContrainte principale
MemoryTests, exemples, développement temporaireDonnées perdues à l’arrêt du processus
SQLiteDéveloppement, tests, applications locales, petite échelleBase monofichier dont l’application gère les réglages
PostgreSQLDéploiements multi-nœuds et bases PostgreSQL existantesL’application gère le pool et l’exploitation de la base
PersonnaliséBases existantes ou intégration métier spécifiqueL’application doit implémenter et vérifier le contrat complet

L’application possède chaque Adapter injecté. Libérez le Service avant l’Adapter :

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

Consultez l’exemple Database Adapter exécutable pour comparer les implémentations.

Footnotes

  1. L’idempotence permet de réessayer une soumission sans appliquer deux fois ses modifications. Conservez le même identifiant : un contenu identique avec un nouvel identifiant constitue une nouvelle soumission. ↩

  2. Le CAS ne modifie une valeur que si elle correspond encore à la valeur attendue. La vérification de la révision et l’écriture suivante doivent être atomiques pour empêcher deux soumissions concurrentes de valider la même révision. ↩

Que pensez-vous de ce guide ?

© 2026 DreamNum Co., Ltd.