Skip to content

System overview

datadata is a library with a clean client/server split. An application talks to it through a session, and two deliberate extension points — the event bus and the storage adapter — decide where and how it runs.

flowchart TB
  accTitle: datadata's client/server split
  accDescr {
    A session sits on top of a client. The client writes a journal and cache to
    an optional persistence adapter, and exchanges events with an event bus.
    The bus exchanges events with the server, which persists through a storage
    adapter. Blob bytes bypass the bus: the client uploads and downloads them
    over HTTP through the host, which persists them in an optional object
    storage adapter.
  }

  session["session<br/>live / staging<br/><small>reads, writes, presence</small>"]
  client["client<br/><small>optimistic overlay</small>"]
  persistence["persistence adapter<br/><small>IndexedDB / memory<br/>optional, shared by tabs</small>"]
  bus["event bus<br/><small>WebSocket / in-process</small>"]
  server["server<br/><small>authoritative</small>"]
  storage["storage adapter<br/><small>SQLite / Postgres / memory</small>"]
  objects["object storage adapter<br/><small>R2 / S3 / fs / memory<br/>optional</small>"]

  session --> client
  client -->|journal + cache| persistence
  client <-->|events| bus
  bus <--> server
  server --> storage
  client <-->|"blob bytes, HTTP<br/>via the host"| objects
  • Session — what the application actually holds. Every read and write goes through one, in one of two kinds: a live session writes straight through, a staging session accumulates a changeset to commit. Both expose the same document API — prepare a document, read it, write it, publish presence — so an app switches lanes without rewriting its call sites. Framework bindings like the React integration hand you a session too.
  • Client — one per connection, shared by the sessions layered on it. Owns subscriptions and the optimistic overlay, and speaks the protocol; a session’s writes land here before they reach the wire.
  • Server — the authority for one folder: validates against schemas, assigns sequence numbers, persists, broadcasts. Enforces limits (document size, rates) and exposes a change callback for automation.
  • Event bus — routes protocol events between clients and server. Implementations: WebSocket (one each for the Durable Object and Node hosts), in-process (server-side agents, tests, demos), and a composite that combines both on one server.
  • Storage adapter — persistence behind the server: documents, the event log, and Yjs state. Two persistent implementations pass the same conformance suites: SQLite in a Durable Object (production) and Postgres behind the async server (not yet in production); tests use memory.
  • Object storage adapter — optional, and the one place bytes live outside the storage adapter: an object store (R2, S3-compatible, filesystem, memory) holding blobs — files referenced from document JSON by immutable handle. The server keeps the catalog and the authorization; the host streams the bytes over HTTP. Without one the server refuses schemas that declare blob fields and nothing else changes.
  • Persistence adapter — the storage adapter’s client-side counterpart, and optional: the pending-write journal and the document cache that let a reload replay its writes and render offline. IndexedDB in browsers, memory in tests. Without one the client is memory-only. See offline persistence.

Tabs on the same (folder, principal) namespace share a persistence adapter, so two offline tabs converge with no server round-trip — the only path in this architecture where clients exchange data without the server.

The production shape is Cloudflare Workers + Durable Objects: one Durable Object per folder, embedding the server, its storage, and its WebSocket connections. The extension points exist precisely so that this is a deployment choice, and a second backend now proves it: one Node process over Postgres, serving many folders from one database through the async server. It passes the same conformance suites as the Durable Object; it has not yet run in production.