← Files taskplaneARCHIVED FILE

lenses/references/database-selection.md

3.46 KB · Oct 2, 2026 · 00:29 UTC

↓ Download file

# Database selection — which engine for which scenario

Applied by the **dba** and **architecture** lenses at requirement/plan time,
when a task introduces or changes a data store. The decision is recorded to
the knowledge base (it's exactly the kind of call the next track must not
relitigate). No pricing here — engine fit first; cost follows fit.

## The four questions that decide it

1. **Shape of the data** — rows with relations? documents that travel
   together? key→value? time-ordered events? connections-as-the-point?
2. **Consistency needs** — do invariants span entities (money, inventory,
   bookings)? Then transactions are non-negotiable.
3. **Query patterns** — known access paths (KV/document thrive) vs ad-hoc
   queries, joins, and reporting (relational thrives).
4. **Write/read profile** — write-heavy append streams, read-heavy lookups,
   or balanced OLTP?

## Default rule

**Start relational (PostgreSQL or equivalent) unless a question above
disqualifies it.** Relational engines handle document columns (JSONB),
moderate KV, full-text, and time-series respectably; the reverse is false.
A specialized engine must earn its place with a workload the relational
default demonstrably can't serve — that justification goes in the R-record.

## Scenario → engine

| Scenario | Reach for | Because |
| --- | --- | --- |
| Business entities with relations, invariants, reporting (orders, users, billing) | **Relational** (PostgreSQL/MySQL/SQL Server) | joins, transactions, constraints enforce invariants at the store |
| Self-contained documents, schema varies per record, known access paths (catalogs, profiles, CMS) | **Document** (MongoDB/Cosmos) — or JSONB in the relational DB first | the document is the unit; no cross-document invariants |
| Hot lookups, sessions, caching, rate counters | **KV/cache** (Redis) *in front of* the system of record | latency; never the only copy of durable data |
| Append-heavy event/metric streams queried by time window | **Time-series** (Timescale/Influx) | retention, downsampling, time-bucketed compression |
| Text search, faceting, relevance ranking | **Search index** (OpenSearch/Meili) *beside* the source of truth | inverted indexes; rebuildable projection, not primary store |
| Similarity/RAG over embeddings | **Vector** (pgvector first, dedicated store at scale) | ANN indexes; pgvector defers a second engine |
| Relationship-traversal as the product (fraud rings, social graphs, ≥3-hop queries) | **Graph** (Neo4j/Gremlin) | recursive joins die where traversals live |
| Massive write throughput, multi-region, query-by-partition-key | **Wide-column** (Cassandra/Cosmos) | linear write scaling; you give up joins & ad-hoc queries knowingly |

## Red flags the lens raises

- A second database engine introduced for a workload the existing one
  handles (polyglot persistence multiplies operational burden — backups,
  migrations, expertise, failure modes — per engine).
- KV/document chosen "for speed" where invariants span records — that's
  buying data corruption with latency savings.
- The cache as the only copy of anything durable.
- A search/vector/analytics store treated as a source of truth instead of a
  rebuildable projection of one.
- No stated migration path for data already living somewhere else.

## Record the decision

`tp.py kb record "DB choice: <workload> → <engine>" --context "<the four
answers>" --decision "<engine + why the default was/wasn't enough>"
--tags db-selection --files "<data-layer globs>"`

SHA-256: 8f8efe7464763940beb60803e1b477cde874f041724496fff993caaf669f69c2