InternalArchitecture
Package Structure & Dependency Rules
How the monorepo is layered, why store-* vs db-* exists, and the dependency graph we enforce.
This page locks in how we organize RippleDB as a monorepo so the core stays portable and we don’t accidentally couple the system to one runtime, database, or UI framework.
The key split: client vs server persistence
RippleDB has two categories of “backend” implementations that must not be blurred:
- Client-side local truth: the database the UI reads from, updated transactionally via replication.
- packages:
@rippledb/store-*
- packages:
- Server-side persistence: the system that accepts changes and serves cursorable incremental history (and may materialize domain tables).
- packages:
@rippledb/db-*
- packages:
If the names don’t encode this split, contributors will mix responsibilities and the design will erode.
Layered package set (v0)
Foundation
@rippledb/core- HLC, tags, Change types, merge/apply helpers (pure logic)
Orchestration (no database drivers)
@rippledb/client- client sync orchestration (outbox, pull/apply/push), policies, stream handling
@rippledb/server- server interfaces/helpers for append + cursor pull, policies
Client truth stores
@rippledb/store-sqlite@rippledb/store-memory(tests)- later:
@rippledb/store-indexeddb, etc.
Server persistence
@rippledb/db-postgres@rippledb/db-sqlite(dev/tests)- later:
@rippledb/db-dynamodb, etc.
UI bindings (optional)
@rippledb/bind-tanstack-query- DbEvent → invalidateQueries, list registry, invalidation coalescing
Dependency graph (must hold)
@rippledb/core→ depends on nothing (or only tiny pure deps)@rippledb/client,@rippledb/server→ depend on@rippledb/core@rippledb/store-*→ depend on@rippledb/core(and optionally@rippledb/clientinterfaces)@rippledb/db-*→ depend on@rippledb/core(and optionally@rippledb/serverinterfaces)@rippledb/bind-*→ depends on@rippledb/coretypes + its UI library; never required by core/client/server
To keep this true over time, we should enforce it with workspace constraints or lint rules.