Changeset 9577c79 for docs/P2-RelationalDesign/RelationalDesign.md
- Timestamp:
- 09/16/26 23:37:15 (13 days ago)
- Branches:
- main
- Children:
- 8b447ef
- Parents:
- df05838
- File:
-
- 1 edited
-
docs/P2-RelationalDesign/RelationalDesign.md (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
docs/P2-RelationalDesign/RelationalDesign.md
rdf05838 r9577c79 11 11 - **Markets**(<u>**id**</u>, *crypto_id*, quote_currency, is_active, created_at) 12 12 - Candidate keys: `{id}`, `{crypto_id, quote_currency}`. `UNIQUE(crypto_id, quote_currency)`. 13 - **Holdings**(<u>**id**</u>, *user_id*, *crypto_id*, quantity, avg_price, created_at, updated_at)13 - **Holdings**(<u>**id**</u>, *user_id*, *crypto_id*, quantity, reserved_quantity, avg_price, created_at, updated_at) 14 14 - Transformation of the M:N relationship `Holds`. Candidate keys: `{id}` and 15 15 `{user_id, crypto_id}` — the latter is the relationship's own key and is … … 17 17 consistency with the other relations. 18 18 - `avg_price` is `NOT NULL DEFAULT 0 CHECK (avg_price >= 0)`. 19 - `reserved_quantity` is `NOT NULL DEFAULT 0 CHECK (reserved_quantity >= 0 20 AND reserved_quantity <= quantity)` — the amount already committed to the 21 user's own open sell orders. `quantity - reserved_quantity` (the amount 22 actually free to sell) is not a stored column; it is computed wherever 23 needed, in `v_portfolio` as `available_quantity` and in the sell path of 24 [UseCase0005](../P3-UseCaseModel/UseCase0005.md). See 25 [ERModel](../P1-ConceptualModel/ERModel.md#holds--users-m--cryptos-n-partial-on-both-sides-with-attributes) 26 for why this mirrors `available_balance`/`invested_balance` on `Users`. 19 27 - **Orders**(<u>**id**</u>, *user_id*, *market_id*, side, type, status, quantity, price, placed_at, executed_at) 20 28 - `side ∈ {buy, sell}`, `type ∈ {market, limit}`, `status ∈ {open, executed, cancelled}`. … … 56 64 ### Normalisation 57 65 66 > **Validated in P5.** [Normalization](../P5-Normalization/Normalization.md) derives this 67 > exact schema independently — starting only from a single de-normalized relation of every 68 > model attribute and its functional dependencies, with no reference to the ER-to-relational 69 > transformation below — and shows it decomposes to **BCNF**, one normal form stronger than 70 > the 3NF claimed here. The two designs agree relation for relation and key for key, so 71 > nothing here changed as a result; see that page's 72 > [discussion](../P5-Normalization/Normalization.md#discussion) for what the one real 73 > difference is (`avg_price`, a stored derived value, not a normalisation issue) and why this 74 > design is still the one used from P5 onward. 75 58 76 All relations are in **3NF**: 59 77 … … 72 90 yields `NULL`, so a nullable average would have silently blanked the 73 91 unrealised-P/L column for an existing position instead of failing loudly. 92 - `holdings.reserved_quantity`, unlike `avg_price`, is **not** derived — it is 93 written directly by the application (`trade.go`) as orders are placed and 94 settled, the same way `quantity` itself is. `quantity - reserved_quantity` 95 ("available") is the derived value here, and it is never stored, only 96 computed where it is needed. 97 98 ### Reservation and the order lifecycle 99 100 `holdings.reserved_quantity` exists so that placing a sell order can be 101 checked against what a user actually has *free* to sell 102 (`quantity - reserved_quantity`), not against the raw `quantity`, which also 103 counts crypto already promised to another order that has not settled yet. 104 `CHECK (reserved_quantity >= 0 AND reserved_quantity <= quantity)` makes an 105 inconsistent reservation impossible at the database level, regardless of what 106 application code does. The exact statement sequence — lock the row, check the 107 available amount, reserve, then settle — is in 108 [UseCase0005](../P3-UseCaseModel/UseCase0005.md); the same 109 `SELECT … FOR UPDATE` locking that already protected `users.available_balance` 110 on the buy path is what makes two concurrent sell orders against the same 111 holding serialize correctly instead of racing. 74 112 75 113 ## DDL script … … 80 118 - 10 tables with check constraints, primary keys, foreign keys and unique constraints. 81 119 - 5 performance indexes. 82 - 2 views: `v_latest_prices` (latest trade price per market) and `v_portfolio` (per-user holdings valuation with unrealised P/L ).120 - 2 views: `v_latest_prices` (latest trade price per market) and `v_portfolio` (per-user holdings valuation with unrealised P/L, plus `reserved_quantity` and the derived `available_quantity`). 83 121 84 122 ## DML script (sample data)
Note:
See TracChangeset
for help on using the changeset viewer.
