One write, one event
Every command commits inside a Postgres transaction that appends exactly one versioned event. A leader-elected relay publishes it to Redis Streams, so nothing is written twice and nothing is lost.
One write path for applications that outgrew a single database. A command commits state, a versioned event and an outbox row in the same Postgres transaction; a leader-elected relay publishes to Redis Streams; the graph (FalkorDB) and search (Elasticsearch) projections are rebuilt from that log rather than written to directly. Tenancy is structural, using row level security, a graph per tenant and index routing, rather than a WHERE clause you can forget.
Every command commits inside a Postgres transaction that appends exactly one versioned event. Nothing is written twice and nothing is lost.
Tenant rides the context into row-level security, graph-per-tenant, index routing and key prefixes. Cross-tenant reads cannot happen.
Neither is written directly. Blue-green rebuilds swap in atomically and a reconciler heals drift through the same outbox.
Structural properties of the fabric, which is the kind you cannot forget to apply.
Every command commits inside a Postgres transaction that appends exactly one versioned event. A leader-elected relay publishes it to Redis Streams, so nothing is written twice and nothing is lost.
Tenant rides on the context and is stamped into row-level security in Postgres, graph-per-tenant, index routing and key prefixes. A cross-tenant read has nowhere to run.
Graph and search are projections. Nothing writes to them directly. Blue-green rebuilds swap in atomically, and a reconciler detects drift and heals it through the same outbox.
Following one write through the fabric, from the facade to every engine that ends up holding it.
A command enters the facade, validated against the registry with tenant and traceparent stamped on the envelope.
State and exactly one versioned event commit atomically, because the outbox lives in the same transaction.
A leader-elected relay wakes on LISTEN/NOTIFY and publishes the event to Redis Streams in order.
Consumer groups project the event into graph and search, redact it for analytics and push live deltas to subscribers.
Reads go through typed ports and writes go through Exec. The fabric decides which engine serves which shape, and your code imports one package.
Typed gets, filtered lists and pagination against the source of truth.
Bulk telemetry ingest and windowed reads on hypertables.
Similarity search over embeddings with HNSW indexes.
Radius and nearest-neighbour search over WKT geometries, in true metres.
openCypher traversals with one-shot, batched hydration.
Full-text multi-match over declared fields, with alias-swap rebuilds.
CRDT documents that materialise into ordinary versioned entities.
External byte storage with content-addressable dedup. Files stay cataloged in Postgres and the bytes never touch it.
Conflated live deltas with Last-Event-ID resume over SSE.
Maintained, ordered, filtered windows with enter, leave and move deltas, exact to top-N.
A typed cube over the tenant’s own events and facts, with metrics that materialise themselves in the background.
A transparent two-level read-through cache behind the read ports, invalidated on write.
One transaction and an outbox; every write emits exactly one versioned event.
The source of truth. Everything else is a projection.
Redis Streams carrying events to graph, search and live queries.
Re-projects the affected window when an observation arrives late.
Fabriq is a data fabric for Go: one transactional write path that fans out to relational, time-series, vector, graph and search engines. It exists because an application that outgrows a single database usually grows a second write in the same handler, and that second write is where silent divergence starts.
Everything else in the design follows from these, and each is enforced structurally rather than by convention.
All of the following has integration coverage:
cmd/fabriq is the whole fabric in one binary. serve runs the worker (outbox relay, projection consumers, reconciler and document plane), and migrate, rebuild, reconcile and inspect are the operator commands.
The property worth the complexity is not exactly-once delivery. It is that dropping a projection entirely is a routine operation rather than an incident.
Shipping something on Fabriq? Nobody is listed here yet. Tell me what you built and you will be the first.
Get listed →