diff --git a/01-RESEARCH/011-the-module-graph/worked-provider.md b/01-RESEARCH/011-the-module-graph/worked-provider.md index d016ac8..838b9a3 100644 --- a/01-RESEARCH/011-the-module-graph/worked-provider.md +++ b/01-RESEARCH/011-the-module-graph/worked-provider.md @@ -107,9 +107,15 @@ That is a property of the consumer, expressible without naming anything. And it that actually decides: a shared need cannot be satisfied by a provider each node runs separately, and a per-instance need should not be satisfied by a shared one. -**Then the mesh binds, and the binding is recorded on the assignment.** Not recomputed. A -resolver that re-derives which store serves a consumer will one day derive a different answer and -relocate a database — so the binding is made once, written down, and changed only deliberately. +**Then the mesh binds, and the binding is written down.** Not recomputed: a resolver that +re-derives which store serves a consumer will one day derive a different answer and relocate a +database, so the binding is made once and changed only deliberately. + +**Where it is written down follows the scope.** A shared grant belongs to the *module* and every +assignment of it references the same one — which is the answer for one module installed on two +nodes wanting one database between them. A per-instance grant belongs to the *assignment*. Same +relation, two homes, and which home is not a detail: it is what makes two instances share +something or not. The pieces that follow, none of them settled here: @@ -153,6 +159,41 @@ case, and reaching it is not the host's job. So the same operation is local at b remote afterwards, which is either two mechanisms or one mechanism with a boundary crossing in it. Undecided, and it is the sharpest unresolved thing in this file. +## Several modules, one database + +Asked directly, and it is three different needs wearing one sentence. They want different +answers and one of them collides with a rule already in force. + +**A grant is not always a whole database.** A provider offers *kinds* of grant, and the kind is +what separates these: + +| The need | The grant | +|---|---| +| my own tables, nobody else's business | a **database** | +| read what another module holds | a **read-only role** on an existing database | +| my own tables *inside* a database others also use | a **schema** within it — a namespace the consumer owns | + +The third is the interesting one, and the one asked about. + +**Loose tables in a shared database is what `how-we-build` §4 warns against**, in as many words: +*contexts integrate through the record, never through a shared schema — today several domains +share one forty-five-table schema, which is why work belonging to one context keeps having to be +implemented in another.* That is not a style objection; it is the observed cost, already paid. + +**A per-consumer schema inside a shared database keeps what the request wants and drops what §4 +objects to.** The consumer gets tables in the same database — same connection, same backup, and +a cross-schema read is physically possible when it is genuinely needed. What it also gets is +ownership: its migrations touch its own namespace, two modules cannot collide over a table name, +and revoking the grant drops the schema rather than guessing which tables belonged to whom. + +Which leaves the fault §4 names — *joining across a boundary* — possible but no longer +accidental. That is the honest position: physically co-located, logically owned, and a +cross-context join is now a thing somebody has to deliberately write rather than the path of +least resistance. + +**And it makes revocation answerable**, which the whole-database version was not. Uninstalling a +consumer drops its schema. Nothing has to work out which of forty-five tables were whose. + ## A migration belongs to the consumer and runs on the provider A game defines migrations. They run against the database the store granted **it**. So the