# SQL vs NoSQL, Plainly

> A fair, two-sided look at relational vs NoSQL databases - what each is actually shaped for, the real trade-offs, and how to choose without joining a holy war.


---

# SQL vs NoSQL, Plainly

You've probably watched this argument play out. One person swears relational databases are
the only serious choice; another insists NoSQL is the modern way and SQL is a relic. Both
sound confident, both have battle scars, and you're left wondering which side is right for
*your* app - usually under deadline pressure, picking a database you'll live with for years.

Here's the straight answer up front: this isn't a fight with a winner. "SQL" and "NoSQL" name
two different *shapes* of data store, each tuned for different problems. The skill isn't
picking the "best" one - it's recognizing which shape fits the problem in front of you. This
guide gives you the mental models and the real trade-offs so you can choose on purpose, not on
hype.

## How to read this

- **Need the decision now?** Jump to [Phase 3: How to Actually Choose](03-how-to-choose.md) - 
  it leads with a plain-language picker and the two warnings that catch people.
- **Want it to finally make sense?** Read in order. Phase 1 builds the mental models, Phase 2
  lays out the real trade-offs, and Phase 3 turns those into a decision you can defend.

## The phases

1. **[The Relational Model & What "NoSQL" Even Means](01-the-models.md)** - what "relational"
   actually is (tables, relationships, SQL), and why "NoSQL" is an umbrella over four very
   different families: document, key-value, wide-column, and graph.
2. **[The Real Trade-offs](02-the-trade-offs.md)** - schema flexibility vs enforced
   integrity, joins vs denormalization, consistency vs horizontal scale, and query power vs
   raw speed - laid out fairly, both sides.
3. **[How to Actually Choose](03-how-to-choose.md)** - the boring-correct default, when to
   reach for a specific NoSQL store, and the two traps ("NoSQL ≠ no schema" and "you can mix").

> This guide is about *choosing* between the families. The deep mechanics of any one store - 
> how to design Mongo documents well, how to tune a wide-column key, how to model a graph - 
> are big enough to deserve their own guides, and we deliberately leave them there.

> 📝 New to databases entirely? Start with [What a Database Is](/guides/what-a-database-is),
> then come back here.


---

# The Relational Model & What "NoSQL" Even Means

Before you can compare two things fairly, you have to know what each one actually *is* - not
the marketing slogan, the real shape. The reason the SQL-vs-NoSQL argument goes in circles is
that one side names a single, well-defined model and the other side names *everything that
isn't that model*. Those aren't symmetric. Once you see why, the whole debate gets calmer. This
phase installs both mental models: what "relational" really means, and why "NoSQL" is a
category, not a product.

## The relational model - tables that know about each other

A relational database stores data in **tables**: rows and columns,
like a spreadsheet with rules. Each table holds one kind of thing (users, orders, products),
and tables are connected by shared values called **keys**. The "relational" part isn't about
the tables being related to *each other* casually - it's a specific math-backed model where a
table is a set of rows and you combine tables by matching keys.

> 📝 **Relation.** In this model, "relation" is the formal word for a table. So "relational
> database" literally means "a database built out of tables." The everyday relationships you
> care about (an order *belongs to* a user) are expressed by storing the user's key inside the
> order row.

You describe your data once as a **schema** - the tables, their
columns, and the types those columns hold - and the database enforces it. Then you ask
questions in **SQL** (Structured Query Language), a declarative language where you say *what*
you want and the engine figures out *how* to get it.

```console
$ psql shop
shop=# SELECT u.name, o.total
shop-#   FROM users u
shop-#   JOIN orders o ON o.user_id = u.id
shop-#   WHERE o.total > 100;
    name     | total
-------------+--------
 Ada Lovelace| 149.00
 Alan Turing | 220.00
(2 rows)
```

*What just happened:* You asked one question that reached across two tables - "give me the name
and order total for every order over 100, with each order matched to the user who placed it."
The `JOIN ... ON o.user_id = u.id` is the relational model doing its core trick: stitching
rows from separate tables together by matching keys, on the fly, at query time. You stored
users and orders separately (no duplication), and the database recombined them when you asked.

> 📝 **Join.** Combining rows from two or more tables by matching a shared value. It's how
> relational databases answer "show me X *together with* its related Y" without storing Y
> inside X.

**Why this is the shape it is.** The relational model was a deliberate design choice: store
each fact in exactly one place, enforce its structure, and let a flexible query language
recombine facts however a future question demands. The payoff is **integrity** (the data can't
easily contradict itself) and **flexibility of questions** (you don't have to predict every
query in advance). The cost is that you commit to a schema and that joins do real work - both
things Phase 2 looks at plainly.

PostgreSQL, MySQL, SQLite, SQL Server, and Oracle are all relational databases. They differ in
features and scale, but they share this model.

## "NoSQL" - an umbrella, not a database

"NoSQL" is the worst-named idea in databases. It doesn't mean "no
SQL" (several NoSQL stores even support SQL-like queries). It started as "non-relational" and
is best read as **"not the relational model."** That's a definition by *absence* - which is
why it covers wildly different tools that have little in common with each other beyond "we
don't do tables-and-joins the relational way."

> ⚠️ **The trap to avoid.** Treating "NoSQL" as a single thing you can compare to SQL is like
> comparing "cars" to "non-cars" - where "non-cars" includes bicycles, boats, and helicopters.
> The useful comparison is always to a *specific* NoSQL family, for a *specific* job.

**The four families, and what each is shaped for.** Almost every NoSQL store falls into one of
four families. Here's the plain one-line version of each - what it is, and the problem it was
built to be good at.

```text
  FAMILY        STORES DATA AS              SHAPED FOR                 TYPICAL TOOL
  ----------    ------------------------    -----------------------    ------------
  Document      self-contained JSON-ish     flexible records you       MongoDB
                documents (a whole object   fetch and update as a
                per record)                 unit

  Key-value     a key → a blob, like a      blazing lookups by key;    Redis
                giant hash map              caching, sessions, counters

  Wide-column   rows grouped by key,        huge write volume across   Cassandra
                spread across many machines many machines, known
                                            query patterns

  Graph         nodes + edges (things and   relationships you traverse Neo4j
                the connections between them) deeply (friends-of-
                                            friends, recommendations)
```

Let's give each a sentence of real shape, because the differences matter more than the label.

**Document (e.g. MongoDB).** A record is a whole document - think a JSON object - and you store
related data *inside* it rather than splitting it across tables. One read gives you the entire
thing.

```console
$ mongosh
> db.users.findOne({ name: "Ada Lovelace" })
{
  _id: ObjectId("64f1a2..."),
  name: "Ada Lovelace",
  email: "ada@example.com",
  addresses: [
    { label: "home", city: "London" },
    { label: "work", city: "London" }
  ]
}
```

*What just happened:* You fetched one user and got their addresses in the same read - because
the addresses live *inside* the user document, not in a separate `addresses` table you'd have
to join. That's the document family's whole pitch: the shape you read is the shape you store,
so common reads are a single lookup. The cost (Phase 2) is that the same address data isn't
sitting in one canonical place the way a relational design would keep it.

**Key-value (e.g. Redis).** The simplest model: a key points to a value, like a dictionary.
You don't query *inside* the value; you get and set by key. It's built to be extremely fast at
exactly that.

```console
$ redis-cli
127.0.0.1:6379> SET session:abc123 "user=42; expires=3600"
OK
127.0.0.1:6379> GET session:abc123
"user=42; expires=3600"
```

*What just happened:* You stored and retrieved a value by its key, with no schema and no query
planning. There's no "find all sessions where..." - that's not what this shape is for. In
exchange for giving up rich queries, you get lookups that are about as fast as a database gets.
This is why key-value stores are the go-to for caches, sessions, and counters.

**Wide-column (e.g. Cassandra).** Data is grouped by a key and physically spread across many
machines, so you can absorb enormous write volume and grow by adding servers. The catch is
that you design your tables *around the queries you'll run* - you decide the access patterns up
front, and ad-hoc questions are awkward.

**Graph (e.g. Neo4j).** Data is nodes (things) and edges (the connections between them), and
the database is built to walk those connections fast. "Friends of friends who like jazz" is a
short, natural traversal here - the same question is a pile of expensive joins in a relational
store. If your *core* problem is relationships several hops deep, this shape earns its keep.

**Why these exist.** Each family is a deliberate trade: it gives up some of the relational
model's generality to be excellent at one access pattern. Document trades canonical-single-copy
for read-it-as-one-unit. Key-value trades querying for raw speed. Wide-column trades ad-hoc
flexibility for write scale. Graph trades table generality for deep-relationship traversal.
None of them is "SQL but better" - each is "different, on purpose."

## Recap

1. **Relational** = data in tables, connected by keys, with an enforced schema, queried with
   **SQL**, where **joins** recombine separate tables at query time. Built for integrity and
   flexible questions.
2. **NoSQL** is an umbrella meaning "not the relational model" - defined by absence, so it
   covers very different tools.
3. The four families: **document** (whole-object records, MongoDB), **key-value** (fast
   lookups by key, Redis), **wide-column** (write scale across machines, Cassandra), **graph**
   (deep relationship traversal, Neo4j).
4. Each family trades some of relational's generality to be excellent at one access pattern.

Now that you know the shapes, we can compare them fairly - which is the next phase.


---

# The Real Trade-offs

This is the phase where most comparisons cheat: they list the strengths of the side they like
and the weaknesses of the side they don't. We're doing it straight - each trade-off gets both
sides, because every one of these is a *trade*: you gain something and give something up.
There's no free lunch, only lunches you're choosing to pay for differently.

The whole phase in one table. Read it, then read the sections below for the *why*.

| The trade-off | Relational gives you | NoSQL (the relevant family) gives you | What you give up either way |
|---|---|---|---|
| **Schema** | Enforced structure - bad data is rejected at the door | Flexible records - change shape without a migration | Relational: friction to change shape. NoSQL: the DB won't catch malformed data for you |
| **Related data** | Joins recombine normalized tables on demand | Denormalization - store related data together for one-read access | Relational: joins do work at query time. NoSQL: duplicated data you must keep in sync |
| **Scaling & consistency** | Strong consistency, easy on one big server | Horizontal scale across many machines | Relational: scaling *out* is harder. NoSQL: often weaker/eventual consistency |
| **Queries vs speed** | Ask any question later, even unforeseen ones | Blazing speed for the *one* access pattern it's tuned to | Relational: general queries aren't always the fastest. NoSQL: off-pattern queries are awkward or slow |

## 1. Schema flexibility vs enforced integrity

**The relational side.** You declare the structure up front, and the database *enforces* it - 
a column typed as a date can't hold "banana," a required field can't be missing, a foreign key
can't point at a user who doesn't exist. This is a guardrail that works while you sleep: every
write is checked.

```console
shop=# INSERT INTO orders (user_id, total) VALUES (999, 'banana');
ERROR:  invalid input syntax for type numeric: "banana"
```

*What just happened:* The database refused a bad write before it could rot your data. You didn't
write that check in your app - the schema *is* the check, across every write from every part of
your system.

**The NoSQL side.** A schema-flexible store (document stores especially) lets each record have
its own shape. Adding a field is just writing it; you don't run a migration across millions of
rows or coordinate a deploy. Early in a project, or when records genuinely *are* irregular,
that's real velocity.

```console
> db.products.insertOne({ name: "Mug", price: 9, color: "blue" })
> db.products.insertOne({ name: "Poster", price: 12, dimensions: "24x36" })
```

*What just happened:* Two products with different fields landed in the same collection with no
complaint - no migration, no schema change. That's the flexibility, and also the catch: nothing
*stopped* the second one from being malformed, because there's no enforced shape. The validation
didn't disappear; it moved into your application code (more in Phase 3).

> 💡 **The clear framing.** This isn't "rigid vs flexible" with flexible winning. It's "the
> database guarantees your structure" vs "you guarantee your structure." Both are valid. One
> trusts the database; the other trusts your code and discipline.

## 2. Joins vs denormalization

**The relational side.** Because each fact lives in one place, you avoid duplication - a
customer's address is stored once, and every order that needs it *joins* to it. Change the
address once and every order sees the new value. Joins are how relational databases stay
**normalized** (no duplicated data) while answering "show me X with its related Y."

> 📝 **Normalized.** Data organized so each fact is stored exactly once, with relationships
> expressed by keys rather than copies. The opposite is **denormalized** - deliberately
> duplicating data so it's pre-combined and fast to read.

**The NoSQL side.** Many NoSQL designs **denormalize** on purpose: they store related data
together (an order document that includes a copy of the shipping address) so a common read is
*one* lookup with no join. For read-heavy paths where you always need the data together, that's
genuinely faster and simpler to fetch.

The cost is the mirror image of the benefit: duplicated data must be kept in sync. If that
address is copied into a thousand order documents and the customer moves, you either update a
thousand copies or accept that old orders show the old address. Sometimes the old address is
*correct* (an order shipped where it shipped), sometimes it's a bug waiting to happen. The trade
is **read simplicity now** vs **update complexity later**.

```mermaid
flowchart TB
  subgraph REL[RELATIONAL - normalized]
    U["users: id, name, address"]
    O["orders: id, user_id, total"]
    O -->|join on user_id| U
  end
  subgraph DOC[NOSQL DOCUMENT - denormalized]
    D["orders: each document holds<br/>total + a COPY of user name &amp; address<br/>(address duplicated per order)"]
  end
```

Relational keeps one address, joined when needed; the document store does one read, but the address is
copied into every order.

The relational "stored once, joined when needed" move on a built-in pair of tables - each book stores
only its `author_id`, and the author's name is recombined at query time:

```sql runnable
SELECT books.title, authors.name AS author
FROM books
JOIN authors ON books.author_id = authors.id;
```
*What just happened:* The author's name lives once in `authors`; the books just point at it by
`author_id`. The JOIN stitched them back together for this question - no name duplicated on disk.

## 3. Consistency vs horizontal scale (a gentle look at CAP)

This is the trade-off with the most folklore around it, so let's keep it grounded.

**The relational side.** A traditional relational database runs on one primary server and offers
**strong consistency**: once a write succeeds, every following read sees it. There's one
authoritative copy, so there's no ambiguity about "the current value" - wonderful for anything
where being wrong for even a moment matters (money, inventory, bookings). The limit is that a
single machine can only get so big; scaling *up* has a ceiling, and scaling *out* (spreading one
logical database across many machines) is genuinely hard while keeping that strong consistency.

**The NoSQL side.** Several NoSQL stores (wide-column especially) are built from day one to
spread across many machines - **horizontal scale**. Add servers, absorb more data and writes.
But spreading data across machines forces a hard question: when a network hiccup splits your
servers apart for a moment, do you refuse writes (stay consistent) or keep accepting them and
reconcile later (stay available)?

> 📝 **CAP, without the jargon.** When your database is spread across machines and the network
> between them fails for a moment, you can't have both perfect consistency *and* full
> availability - you have to favor one. Many distributed NoSQL stores favor availability and
> offer **eventual consistency**: a write shows up everywhere *soon*, but a read right after a
> write might briefly see the old value.

**The clear framing.** Strong consistency on one machine is easy and is the relational default.
Massive horizontal scale is easy for distributed NoSQL stores and is their default. Getting
*both at once* is the genuinely hard engineering problem - which is why "guaranteed-correct-
right-now, or scale-past-one-machine?" is one of the most useful questions you can ask about an app.

⚠️ **Don't over-rotate on scale.** A single modern relational server, properly indexed, handles
far more load than most apps will ever see. "I might need to scale to billions of rows someday"
is rarely a reason to give up strong consistency *today*.

## 4. Query power vs raw speed for a known pattern

**The relational side.** SQL lets you ask questions you didn't anticipate when you designed the
schema. New report next quarter? Write a new query - the data model doesn't have to change,
because you can always recombine tables a new way. That open-ended query power is the relational
model's quiet superpower: you're not locked into the questions you thought of on day one.

**The NoSQL side.** When you know your access pattern in advance and tune the store for it,
NoSQL can serve that *specific* pattern extremely fast - a key-value `GET`, a single-document
read, a wide-column lookup by its partition key. The store is shaped exactly like the question,
so there's almost no work to do at read time. The catch is the flip side: ask a question the
store *wasn't* shaped for, and it's awkward or slow. "Find all sessions belonging to users in
Canada" is trivial SQL and nearly impossible to ask a plain key-value cache, which only knows
how to look up by key.

> 💡 **The clear framing.** Relational optimizes for *which questions you can ask*; tuned
> NoSQL optimizes for *how fast one known question runs*. Faster-at-the-thing-it's-built-for is
> real, but narrow - "is it faster?" only means anything once you name the query. "NoSQL is
> faster than SQL" with no access pattern attached is a bumper sticker, not a true sentence.

## Recap

1. **Schema:** the database guarantees your structure (relational) vs you guarantee it
   (flexible NoSQL). Validation moves, it doesn't vanish.
2. **Related data:** joins on normalized tables (no duplication, work at read time) vs
   denormalization (one-read speed, duplication to keep in sync).
3. **Consistency vs scale:** strong consistency on one machine vs horizontal scale with often
   eventual consistency. Having both at once is the hard problem (that's CAP).
4. **Queries vs speed:** ask anything later (SQL) vs blazing speed for the one pattern you
   tuned for (NoSQL), at the cost of off-pattern queries.

Every row of that opening table is a real trade. The next phase turns these into an actual
decision.


---

# How to Actually Choose

You've got the mental models and the real trade-offs. Now the part that actually feels hard
under deadline pressure: picking one. This phase is mostly **judgment**, flagged as judgment
where it is - weigh it against your own situation, don't take it as law. But there's a default
that's right far more often than the internet's energy around this topic would suggest, and
two traps that catch people who pick NoSQL for the wrong reason.

## The picker

> **Find the row that matches your situation, then read the section below it.**

| Your situation | The straight move | Why |
|---|---|---|
| "It's a normal app - users, things they own, relationships between them." | **Relational** (Postgres, MySQL, SQLite). | The boring-correct default. Integrity + flexible queries cover most apps for years. (§1) |
| "I need to make repeated reads blazing fast / store sessions / counters." | Add a **key-value cache** (Redis) *alongside* your main DB. | A cache is a *complement*, not a replacement. (§2) |
| "I'm absorbing a firehose of writes across many machines." | Consider **wide-column** (Cassandra) for that workload. | Built for write scale past one machine. (§2) |
| "My core problem is deep relationships - friends-of-friends, recommendations." | Consider a **graph** store (Neo4j) for that part. | Deep traversal is its native strength. (§2) |
| "My records are genuinely irregular / document-shaped and read as a unit." | Consider a **document** store (MongoDB). | Flexible, read-it-as-one-unit records. (§2) |
| "I picked NoSQL because 'no schema' sounds easier." | Stop - read §3 first. | The schema didn't vanish; it moved into your code. |
| "I think I need more than one of these." | You probably do, and that's fine - §4. | Polyglot persistence is normal. |

## 1. The boring-correct default: a relational database

**The judgment, stated plainly:** *for most applications, a relational database is the right
default, and you should need a specific reason to choose otherwise.* That's an opinion, but a
well-worn one, and here's the reasoning so you can decide if it applies to you.

Most apps are, underneath, a set of *things* with *relationships* between them: users have
orders, orders have line items, line items reference products. That's the relational model's
home turf. You get integrity for free (Phase 2, §1), you can ask questions you haven't thought
of yet (Phase 2, §4), and a single well-indexed relational server handles more load than the
vast majority of apps will ever reach.

The reason "boring" is a compliment here: a relational database is mature, deeply understood,
documented everywhere, and unlikely to surprise you at 2am. Choosing it is rarely the decision
you regret; choosing something exotic *without a reason* is.

⚠️ **"We might need web-scale someday" is not a reason today.** Picking a distributed NoSQL
store to handle traffic you don't have yet means paying its costs - weaker consistency, harder
queries, more operational complexity - *now*, for a problem that may never arrive. If it does
arrive, you'll have real numbers to design against. Optimize for the app you have.

## 2. Reach for a NoSQL store for a *specific* access pattern

The right way to bring in NoSQL is **a specific store for a specific job**, not "let's be a
NoSQL shop." Each family from Phase 1 earns its place when its access pattern is *your*
problem:

- **Cache / sessions / counters → key-value (Redis).** When the same expensive read happens
  over and over, put a fast key-value store in front of it. This is the most common and most
  clearly-justified NoSQL adoption - and it sits *alongside* your relational database, not
  instead of it.
- **Huge write volume across machines → wide-column (Cassandra).** Time-series, event logs,
  sensor data, activity feeds at enormous scale - workloads where writes-per-second outgrow a
  single server and your queries are known in advance.
- **Deep relationship traversal → graph (Neo4j).** When the *queries themselves* are about
  walking connections many hops deep (social graphs, fraud rings, recommendation paths), a
  graph store does natively what would be punishing joins in SQL.
- **Genuinely document-shaped, irregular records → document (MongoDB).** When your records
  really are self-contained objects of varying shape that you read and write as a whole, a
  document store fits the grain of the data.

The test for all four: **can you name the access pattern out loud?** "I need sub-millisecond
lookups by session ID" is a reason. "It's more modern" is not.

🪖 **A common arc.** Plenty of teams start on a relational database, run fine for years, then
add Redis the day a hot query starts hurting - relational core, a specialized store bolted on
for one measured problem. The unhealthy pattern is the reverse: choosing an exotic store first
and discovering later you've made the *normal* parts of the app harder.

## 3. ⚠️ Trap one: "NoSQL ≠ no schema"

This is the most expensive misconception in the whole topic, so it gets its own section.

A schema-flexible store doesn't mean your data has no schema. Your data **always** has a
structure - your code reads `user.email` and `order.total` and expects them to exist and be the
right type. The only question is **who enforces that structure.**

- In a relational database, the *database* enforces it. A bad write is rejected at the door
  (you saw `ERROR: invalid input syntax` in Phase 2).
- In a schema-flexible store, *your application code* enforces it - or nobody does, and then
  malformed records pile up silently until something downstream chokes on them.

```mermaid
flowchart LR
  subgraph R[Relational]
    RA[your app] --> RD[DATABASE enforces shape] --> RC[data is clean]
  end
  subgraph S[Schema-flexible]
    SA["your app enforces shape<br/>(skip this → malformed data gets in)"] --> SD[database stores anything] --> SQ["?"]
  end
```

The plain version: "schemaless" really means "the schema moved out of the database and into
your code and your discipline." That can be the right trade, but it's a *relocation* of the
work, never a *deletion* of it. Teams that picked NoSQL expecting to skip data modeling
entirely tend to rediscover, painfully, that the modeling was load-bearing.

> 💡 **Key point.** You never get to *not* have a schema. You only get to choose whether the
> database guards it or you do.

## 4. ⚠️ Trap two: it's not either/or - you can mix

The framing "SQL *vs* NoSQL" quietly implies you must pick one for your whole app. You don't - 
real systems routinely use several stores, each for what it's best at. There's even a name for
it: **polyglot persistence.**

> 📝 **Polyglot persistence.** Using more than one type of data store in a single system,
> matching each store to the job it fits, instead of forcing everything into one.

A very ordinary, healthy architecture:

```mermaid
flowchart LR
  PG["PostgreSQL<br/>source of truth: users, orders, products<br/>(relational - integrity + flexible queries)"]
  PG --> Redis["Redis<br/>cache hot reads + sessions<br/>(key-value, speed)"]
  PG --> ES["Elasticsearch<br/>full-text product search<br/>(a search-tuned store)"]
```

Nothing about this is exotic or contradictory. Postgres is the authoritative record; Redis
makes the hot path fast; a search engine handles fuzzy text queries that SQL is clumsy at. Each
store does the one thing it's shaped for, and the relational database stays the single source
of truth the others derive from.

The cost of mixing is real and worth naming: more moving parts to operate, more places data can
drift out of sync, more for a new teammate to learn. Mix *deliberately* - add a store when a
measured problem justifies it, not because variety feels sophisticated.

## Recap

1. **Default to relational.** For most apps it's the boring-correct choice - integrity,
   flexible queries, maturity. You should need a *reason* to deviate.
2. **Reach for a specific NoSQL store for a specific, nameable access pattern** - cache
   (key-value), write-firehose (wide-column), deep traversal (graph), irregular documents
   (document).
3. **"NoSQL ≠ no schema."** The schema moves into your code; it never disappears. Decide who
   guards it on purpose.
4. **You can mix.** Polyglot persistence - a relational core plus specialized stores - is
   normal and often right. Mix deliberately, because each store adds operational cost.

That's the whole clear picture: not a winner, but a set of shapes and trades you can now reason
about. Pick the shape that fits the problem in front of you, name your reason, and you'll defend
a real decision instead of taking a side in a holy war.

## Where to go next

- [What a Database Is](/guides/what-a-database-is) - the groundwork beneath this whole comparison.
- [Relationships and Keys](/guides/relationships-and-keys) - how the relational model actually
  links tables, in depth.
- [Scaling a Database](/guides/scaling-a-database) - the "scale up vs scale out" and consistency
  story from Phase 2, taken further.
