Keep your SQL tables as the single source of truth. Documents, search indexes, vectors and caches are projections of those tables that the database builds, shards and keeps consistent on every write. Start by attaching to the Postgres you already run, with no migration.
| Item | Part |
|---|---|
| 1 | TablesThe source of truth. Full SQL, joins and transactions, sharded across nodes. |
| 2 | Projection engineRe-derives what each write touches, holds every projection to its freshness, audits for drift. |
| 3 | ProjectionsDocuments, search, vectors, caches, events and analytics, each a query over the tables. |
| 4 | InterfacesPostgres wire, MongoDB wire and HTTP. A write token makes any of them read your writes. |
| 5 | Your appsOrdinary drivers, unchanged. |
| 6 | AttachFollows the Postgres you already run until you decide to cut over. |
exploded view of the stackin red: each write, re-deriving the projections it touches
| Sheet1/6 | TitleThe stack | Rev0.28.0 |
Products start on Postgres. Then come MongoDB for nested documents, Elasticsearch for search, Redis for hot keys and a vector store for embeddings. Every sync job between them is a place where data goes missing or stale, and no transaction spans them.
| Sheet2/6 | TitleThe problem | Rev0.28.0 |
You don't migrate on day one. IsomerDB follows your existing database, builds what you read, and takes over as the primary only when you decide to cut over.
Point IsomerDB at your Postgres. It checks every table for keys and replica identity, copies it, then follows the change stream. Table names stay the same, and nothing is migrated.
SELECT * FROM isomer.attach( 'shop', 'host=... dbname=... user=...');
Write the shape you want to read as a SQL query. The database builds it, shards it beside the rows it comes from, and certifies it.
SELECT isomer.create_projection( name => 'customer_profile', kind => 'document', key => 'id', query => $$ SELECT c.id, jsonb_build_object('name', c.name, 'orders', (SELECT jsonb_agg(o.status) FROM orders o WHERE o.customer_id = c.id)) AS doc FROM customers c $$);
Each write re-derives only the keys it touches. Aggregates across shards are maintained incrementally from row deltas, and a cost model picks the strategy.
UPDATE customers SET name = 'Asha' WHERE id = 42; SELECT isomer.last_write_token(); -- carry it to the read
Read the projection from SQL, as a MongoDB collection, or over HTTP. Pass the write token and the read waits for exactly that write. Causally consistent MongoDB sessions do it with no code changes.
# MongoDB driver, unchanged db.customer_profile.findOne({ _id: 42 }) # HTTP, waiting for your write curl -H "X-Isomer-After: $TOKEN" \ localhost:8080/v1/projections/customer_profile/docs/42
When you're ready, IsomerDB freezes the source, catches up, copies sequences and defaults, and becomes the primary. It can replicate back for a rollback. A cutover plan lists everything left to recreate first.
SELECT * FROM isomer.cutover('shop', rollback_conninfo => 'host=... user=...'); SELECT isomer.alter_projection('customer_profile', freshness => 'sync');
| Sheet3/6 | TitleHow it works | Rev0.28.0 |
You choose how fresh each projection must be, and the database holds to it.
Updated inside the writing transaction. A read never sees the table and the projection disagree. Available once IsomerDB is the primary.
Maintained right after the write, within a lag budget such as 2 seconds. A breach raises an alert, and a read that passes a write token waits until that write has landed.
| Sheet4/6 | TitleFreshness | Rev0.28.0 |
Each one is a query over the same tables. Drop it and the data is still there; change the query and it is rebuilt.
read as a MongoDB collection
Nested documents built with joins and subqueries, kept current on every write.
BM25 from SQL
Full-text search ranked with BM25, over joins across sharded tables.
nearest neighbour in SQL
Only rows whose text changed are re-embedded, with a hosted model or bge-small on your own CPU.
hot keys, by key
Key-value entries derived from the tables, so there is nothing to invalidate by hand.
a change feed, or live over HTTP
Domain events from row changes, such as order_paid, in commit order per row.
columnar tables
Aggregates maintained incrementally: a change rewrites only the days or groups it reaches.
replicas close to your users
Projections served from memory over HTTP and MongoDB, fed by the change feed.
by the database itself
It reads its own query log, proposes the projections and shard keys it should have, and routes a slow query only after checking the rows match.
| Sheet5/6 | TitleProjections | Rev0.28.0 |
The same data kept in Postgres, MongoDB, Elasticsearch, Redis and a vector store, synced by CDC pipelines, against one database that keeps its own projections. Every claim on the right is checked while it runs.
| Question | Postgres + sync pipelines | IsomerDB |
|---|---|---|
| Does a write land everywhere at once? | No. Each store commits on its own, so a crash between them leaves the copies disagreeing. | Yes. Sync projections change in the writing transaction; bounded ones follow within a lag budget you set. |
| Can I read my own write from another store? | Not reliably. A read can come back stale until the consumer catches up, and nothing tells you when it has. | Yes. Pass the write token and the read waits for that write, over SQL, MongoDB or HTTP. |
| How fresh is each copy? | Whatever the pipeline manages that day. Seconds is normal; a backfill can take it to minutes. | Declared per projection, measured all the time, and an alert fires on a breach. |
| How do I find a copy that has drifted? | Usually a customer reports it. | The live auditor recomputes samples from the tables and pages on-call on any difference. |
| How do I know it's correct? | Each store is tested on its own. Nothing tests the pipelines between them. | Each projection carries a certificate of the rules it relies on, and Jepsen runs nightly with nodes killed, paused and partitioned. |
| What do I operate? | Five databases, Kafka, connectors and the consumers you wrote. | One database. |
IsomerDB column: Jepsen nightly run of 23 October 2026, five workloads valid (isolation across shards, read-your-writes over SQL and MongoDB, incremental and subquery projections). The workloads have found and fixed three bugs so far.
| Sheet6/6 | TitleCompared | Rev0.28.0 |
take it for a drive
Pull the released images and you have Postgres, the MongoDB gateway and the console on your machine. For production, the Kubernetes operator runs hot standbys, fails over, backs up and upgrades.
# needs Docker with Compose v2
git clone https://github.com/Ash20pk/IsomerDB.git && cd IsomerDB
ISOMER_REGISTRY=ghcr.io/ash20pk/ ISOMER_TAG=0.28.0 \
docker compose up -d --no-build
./scripts/smoke.sh
psql postgres://postgres:isomer@localhost:5432/postgres