3.7 Decision cheat sheet
Document if the data is a tree of one-to-many relationships typically loaded whole, the items are genuinely one-to-few, and you rarely need to reference nested items directly.
Relational or document?
Document if the data is a tree of one-to-many relationships typically loaded whole, the items are genuinely one-to-few, and you rarely need to reference nested items directly. Relational if you have many-to-one/many-to-many, need to address items by ID, or need joins.
In practice, use a hybrid — Postgres with jsonb columns is the default correct answer for most applications, and the convergence trend says so explicitly.
Normalize or denormalize this specific field? Ask two questions: How fast does it change? (fast → normalize + hydrate) and What dominates cost — reads or writes, and are they dominated by outliers? Don't answer per-table; answer per-field. The X timeline denormalizes the join result and normalizes the contents.
Schema-on-write or schema-on-read? Schema-on-write when records are expected to have the same structure — a schema then documents and enforces it. Schema-on-read when data is heterogeneous (too many object types to table each one) or the structure is controlled by an external system that may change at any time.
When do I actually need a graph database? When queries traverse a variable number of hops not known in advance, over data with arbitrary many-to-many connectivity. If your traversals are always exactly 2 hops, a relational join is fine and cheaper. Beware supernodes and don't plan on sharding.
Should I use event sourcing? Yes if: intent matters, auditability is required, you need multiple divergent read models, or reversibility is valuable. No if: the domain is CRUD, you have no need for history, or you can't commit to keeping event-processing deterministic and replayable forever. Half-hearted event sourcing is worse than none.
Star, snowflake, or OBT? Star by default — simpler for analysts. Snowflake when dimension normalization actually pays. OBT when storage is cheap and you need the join gone.