Align the OpenUP markdown set to the blank templates (process-data repo, templates/openup/) per BUILD-CHECKLIST 7. Scenario step text unchanged; the EA model needs no change (brief descriptions already live in the model's UC/actor notes). - vision.md: positioning tables, stakeholder summary, user environment, needs-and-features table (R1/R2), other product requirements - use-case-model.md: per-UC specification sections (brief description, actor descriptions, named alternative/exception subsections with numbered steps, post-conditions, special requirements) - supplementary-specifications.md: category tables with priority and use-case columns + reverse coverage matrix; IDs unchanged - test/quality-check-procedure.md: migrated from Joplin (single content home); stale open items marked resolved (WI-1/WI-2) - analysis-design/sequences/: 12 PlantUML design sequences (lifelines = analysis classes, messages = design elements; per the PlantUML layout style guide) + PNG renders; embedded in Architecture Notebook 9.3 - Joplin consolidated: project content notes deleted (all live in docs/game + docs/openup); Joplin keeps the status note only
271 lines
16 KiB
Markdown
271 lines
16 KiB
Markdown
# Skara Brae — Architecture Notebook
|
||
|
||
Version 1.0 (2026-10-05) · OpenUP Analysis & Design artifact: `analysis-design/architecture-notebook.md`
|
||
Summary work product in the keyfob-station template structure. Sources (all in-repo, normative):
|
||
`analysis-design/uc-realization-mapping.md` (§1–§3), `analysis-design/design-wi-2-architecture-cleanup.md`
|
||
(§2–§4, §11), `requirements/supplementary-specifications.md`, `requirements/vision.md`, and the EA model
|
||
`docs/openup/model/skara-brae.eapx` (exports under `docs/openup/model/exports/`). Detailed per-WI design
|
||
remains in the WI design notes; this notebook is the cross-WI architecture view. Drift rule applies:
|
||
a mismatch between this notebook and its sources is a defect fixed in the same cycle.
|
||
|
||
## 1. Purpose
|
||
|
||
This notebook describes the goals, decisions, constraints and significant elements that shape the design of
|
||
Skara Brae across work items. The analysis and design models live in the EA model (Requirements / Use Case
|
||
Model, Analysis-Design realizations and Design component model); this notebook summarizes them for review
|
||
and onboarding. One model covers the whole product — server, client and common — because the use cases are
|
||
player-facing and Requirement→realization traceability spans tiers (joint-model decision, 2026-10-03).
|
||
|
||
## 2. Architectural Goals and Philosophy
|
||
|
||
- **Dependency rule inward** (Clean Architecture): domain in `sb-core`/`sb-shared` with zero HTTP/framework
|
||
knowledge; adapters (`sb-api`, `sb-db`, `sb-auth`) and the client at the edge (D1, CON-05).
|
||
- **Behavior continuity**: refactors are wire-format and game-semantics identical (NFR-11, NFR-19).
|
||
- **Determinism by construction**: domain randomness is injected (WI-1); identical seeds produce identical
|
||
outcomes (NFR-05, D5).
|
||
- **One taxonomy of failure**: a single domain error enum with one central HTTP status mapping (D2).
|
||
- **Data as source of truth**: game content is language-neutral JSON in `skara-brae-common`, compiled into
|
||
both consumers (NFR-13, D6).
|
||
- **Zero-warning quality gate**: fmt + clippy + full test suite on every commit (NFR-08..NFR-10).
|
||
- **Docs are architecture**: OpenUP markdown is the content source; the EA model renders it; drift is a
|
||
defect (D7).
|
||
|
||
## 3. Assumptions and Dependencies
|
||
|
||
- Single-process Rust server (Axum 0.8, edition 2024); all game state in memory behind `tokio::sync::RwLock`,
|
||
optional write-through persistence to SQLite via `SB_DB_PATH` (NFR-01, NFR-14).
|
||
- Static game data ships with the binary via `include_str!` from `skara-brae-common` (NFR-04, NFR-13).
|
||
- Clients are web/PWA (Vue 3 SPA) polling the REST API; server behavior is poll-frequency-independent
|
||
(NFR-02, NFR-03).
|
||
- `sb-common` is consumed as a sibling absolute-path dependency locally; git-submodule migration planned
|
||
(CON-01).
|
||
- Self-hosted pipeline: Forgejo (git), Jenkins CI, systemd deployment (vision.md; topology canon in Joplin
|
||
"Tech Stack & Architecture" note).
|
||
|
||
## 4. Architecturally Significant Requirements
|
||
|
||
The full set is `requirements/supplementary-specifications.md`; the categories that shape the architecture:
|
||
|
||
| Category | IDs | Architectural consequence |
|
||
|---|---|---|
|
||
| Performance / Scalability | NFR-01..NFR-04 | in-memory state, lazy regen (no timers), lock-free catalogue reads |
|
||
| Reliability / Correctness | NFR-05..NFR-07 | injected RNG, best-effort persistence, referential data integrity → `DomainError` |
|
||
| Quality Gates | NFR-08..NFR-12 | fmt/clippy/test gates; unwrap ledger; behavior-identical refactors |
|
||
| Data | NFR-13..NFR-15 | bilingual common package, SQLite format, i64 stat growth |
|
||
| Security | NFR-16..NFR-18 | argon2, JWT HS256 + auth middleware, role model (player/admin) |
|
||
| Constraints | CON-01..CON-06 | crate layout, Axum 0.8, rand 0.9 rule, one error enum, dependency rule, docs home |
|
||
| Usability / API | NFR-19..NFR-20 | wire-format freeze (lexicographic key order), status-code contract |
|
||
|
||
## 5. Decisions, Constraints, and Justifications
|
||
|
||
Full records in the WI design notes; the architecture-level decisions:
|
||
|
||
- **D1 — Layered crate architecture with inward dependencies.** `sb-api` (boundary/adapter) may import
|
||
`sb-core`, `sb-shared`, `sb-common`, `sb-db`, `sb-auth`; `sb-core` must not reference axum/http/
|
||
serde_json-json!/tokio/sb-db/sb-api (design note §3). Known debt: ShopService, HealingService, parts of
|
||
MagicService remain handler-side in sb-api — recorded, follow-up WI question Q3; the model must not paper
|
||
over them.
|
||
- **D2 — One domain error taxonomy.** `sb_core::error::DomainError` (thiserror 2) + single status mapping
|
||
`sb-api::routes::domain_status` realize the error paths of UC-05/06/08/12 (design note §4.1).
|
||
- **D3 — Constants centralized.** `sb_core::constants` is the single audit home for game-balance scalars
|
||
(NFR "no magic numbers"); static data tables stay in their modules (design note §2).
|
||
- **D4 — Response DTOs in sb-api, not sb-shared.** HTTP wire types never leak into the domain crate;
|
||
`AdventureOutcome` derives nothing (design note §2 placement decisions).
|
||
- **D5 — Injectable RNG with a single construction point.** `request_rng()` (`StdRng::from_os_rng`, WI-1);
|
||
domain functions take `rng: &mut impl Rng` (CON-03, NFR-05).
|
||
- **D6 — Bilingual shared data package.** `skara-brae-common`: `data/` JSON single source, `rust/` + `ts/`
|
||
thin consumers, `codegen/` planned (NFR-13; tech-stack canon).
|
||
- **D7 — Joint EA model, markdown source of truth, surgical builders.** Model changes after the manual
|
||
layout session go through surgical scripts only; `build-model.ps1` rebuilds and is forbidden on the laid-
|
||
out model (mapping §1, BUILD-CHECKLIST §6).
|
||
|
||
## 6. Architectural Mechanisms
|
||
|
||
- **Lazy resource regeneration** — resources recompute from elapsed wall-clock time at specific call sites;
|
||
no timers or background ticks (NFR-02; UC-05/UC-08 "Regenerate Resources" sub-flow).
|
||
- **Static catalogue via LazyLock** — game data parsed once at first access; lock-free reads (NFR-04, UC-04).
|
||
- **DomainError → status mapping** — domain rule failures flow through one enum to one mapping; handlers own
|
||
store misses/authz (D2, NFR-20).
|
||
- **Wire-format freeze** — JSON keys serialize in lexicographic order; typed responses declare fields in that
|
||
order (NFR-19).
|
||
- **Injectable RNG determinism** — seeded domain tests, single entropy entry point (D5, NFR-05).
|
||
- **Best-effort write-through persistence** — SQLite behind an in-memory-authoritative process (NFR-06,
|
||
NFR-14).
|
||
|
||
## 7. Key Abstractions
|
||
|
||
The analysis vocabulary (boundary/controller/entity) of the use-case realizations — full mapping incl.
|
||
design elements in `uc-realization-mapping.md` §2; classes, stereotypes and interactions per UC in the EA
|
||
model (§2a lists the 24 associations + 4 «uses» dependencies).
|
||
|
||
| Class | Stereotype | Responsibility (design element) |
|
||
|---|---|---|
|
||
| AuthBoundary | «boundary» | `POST /api/auth/register`, `AuthResponse` (UC-01) |
|
||
| TokenService | «boundary» | `POST /api/auth/login`, `AuthResponse`, auth middleware (UC-02, guards UC-03…12) |
|
||
| CharacterCatalog | «boundary» | character routes (UC-03) |
|
||
| CatalogueBrowser | «boundary» | catalogue routes + `sb-shared::responses` (UC-04) |
|
||
| GymBoundary | «boundary» | gym routes + responses (UC-05) |
|
||
| EquipmentBoundary | «boundary» | equip/unequip routes (UC-06) |
|
||
| ShopBoundary | «boundary» | shop routes + responses (UC-07) |
|
||
| AdventureBoundary | «boundary» | adventure route + `AdventureRunResponse` (UC-08) |
|
||
| TempleBoundary | «boundary» | heal route + `HealResponse` (UC-09) |
|
||
| SongBoundary | «boundary» | song routes + responses (UC-10) |
|
||
| MagicBoundary | «boundary» | spell routes + responses (UC-11) |
|
||
| ReviewBoundary | «boundary» | level-up route + response (UC-12) |
|
||
| AccountRegistry | «controller» | `sb-api::routes::{register, seed_users}` (UC-01) |
|
||
| Authenticator | «controller» | `sb-api::routes::login` + sb-auth verify/create (UC-02) |
|
||
| CharacterFactory | «controller» | `sb-core::character::create_character` (UC-03) |
|
||
| TrainingService | «controller» | `sb-core::services::train_stat` (UC-05) |
|
||
| EquipmentService | «controller» | `sb-core::services::{equip_item, unequip_item}` (UC-06) |
|
||
| ShopService | «controller» | `sb-api::routes::{shop_action, buy_item, sell_item}` (UC-07; **debt**: handler-side, extraction candidate Q3) |
|
||
| AdventureService | «controller» | `sb-core::adventure::run_adventure` (UC-08; WI-2 extraction) |
|
||
| HealingService | «controller» | `sb-api::routes::heal_character` (UC-09; **debt**: handler-side, extraction candidate Q3) |
|
||
| SongService | «controller» | `sb-api::routes::set_song` + `available_songs` (UC-10) |
|
||
| MagicService | «controller» | spell routes + `sb-core::spells` helpers (UC-11; **debt**: parts handler-side) |
|
||
| ReviewBoard | «controller» | `sb-core::character::level_up` (UC-12) |
|
||
| UserAccount | «entity» | `sb-shared::user::{User, Role}` (UC-01/02) |
|
||
| Character | «entity» | `sb-shared::character::Character` + Race/Class/Stats (UC-03) |
|
||
| ResourceRegenerator | «entity» | `sb-core::character::apply_regen` + regen constants (UC-03/05/08 sub-flow) |
|
||
| GameData | «entity» | `sb-core::data::{EnemyDef, ZoneDef, EncounterEntry, LootDrop}` (UC-04) |
|
||
| TrainingGrounds | «entity» | `TRAINING_GROUNDS`, `training_cost` (UC-05) |
|
||
| Equipment/Inventory | «entity» | `Character.equipment/.inventory`, `sb-shared::item::Item` (UC-06) |
|
||
| ShopStock | «entity» | `AppState.shop_stock` (UC-07) |
|
||
| CombatEngine | «entity» | `sb-core::combat::{resolve_combat_full, resolve_attack, crit tables, open_ended_roll}` (UC-08) |
|
||
| Combatant | «entity» | `sb-core::combat::CombatStats` (UC-08) |
|
||
| SpellEffect / BardSong | «entity» | `SpellEffect`, `sb-core::spells`, `sb-core::songs` (UC-08/10/11) |
|
||
| BardSong | «entity» | `BARD_SONGS`, `Character.active_song` (UC-10) |
|
||
| Spell | «entity» | `SpellDef`, `Character.{known_spells, active_spell, spell_points}` (UC-11) |
|
||
| Progression | «entity» | `xp_for_level`, `level_up_cost`, `MAX_LEVEL` (UC-12) |
|
||
|
||
Cross-cutting: `DomainError` (error paths UC-05/06/08/12), `Constants` (no-magic-numbers NFR), `Responses`
|
||
(wire-format freeze NFR-19) — carried in the model's Architecture Overview package.
|
||
|
||
## 8. Layers
|
||
|
||
The joint component model (mapping §3); dependencies point inward only.
|
||
|
||
| Tier | Component | Role |
|
||
|---|---|---|
|
||
| Presentation | skara-brae-client | Vue 3.5 SPA (Pinia, Vite), JWT bearer, REST via proxy; player-facing boundary of UC-01…12 |
|
||
| Boundary / adapter | sb-api | Axum routes/handlers, `domain_status`, `responses`; boundaries of all UCs |
|
||
| Adapter | sb-auth | argon2 + JWT (UC-01/02; middleware guards UC-03…12) |
|
||
| Adapter | sb-db | persistence via `sb-db::Database` |
|
||
| Domain | sb-core | controllers/entities: adventure, services, character, combat, spells, songs, data/items |
|
||
| Domain types | sb-shared | `User`, `Character`, `Item`, response types; zero HTTP imports |
|
||
| Data | sb-common | bilingual data package (`data/` JSON, `rust/`, `ts/`, `codegen/`); UC-04 data source |
|
||
|
||

|
||
|
||
*Figure 1: Joint component architecture — server crates, client, common (EA model render).*
|
||
|
||
## 9. Architectural Views
|
||
|
||
### 9.1 Use Case View
|
||
|
||
Architecturally significant use cases: UC-08 (Run Adventure — WI-2 design driver, combat + data integrity),
|
||
UC-03/UC-12 (character lifecycle), UC-04 (shared-data consumption), UC-05/06/07 (rule-driven economy
|
||
actions with the DomainError error paths), UC-01/02 (auth, middleware guard). Full model:
|
||
`requirements/use-case-model.md` + overview figure there.
|
||
|
||
| Realization | Use Case | Analysis classes |
|
||
|---|---|---|
|
||
| UC-01 Realization | Register Account | AuthBoundary, AccountRegistry, UserAccount |
|
||
| UC-02 Realization | Authenticate | TokenService, Authenticator |
|
||
| UC-03 Realization | Manage Characters | CharacterCatalog, CharacterFactory, Character, ResourceRegenerator |
|
||
| UC-04 Realization | Browse Game Catalogue | CatalogueBrowser, GameData |
|
||
| UC-05 Realization | Train Stat | GymBoundary, TrainingService, TrainingGrounds |
|
||
| UC-06 Realization | Manage Equipment | EquipmentBoundary, EquipmentService, Equipment/Inventory |
|
||
| UC-07 Realization | Trade at Shop | ShopBoundary, ShopService, ShopStock |
|
||
| UC-08 Realization | Run Adventure | AdventureBoundary, AdventureService, CombatEngine, Combatant, SpellEffect / BardSong |
|
||
| UC-09 Realization | Heal at Temple | TempleBoundary, HealingService |
|
||
| UC-10 Realization | Practice Bard Song | SongBoundary, SongService, BardSong |
|
||
| UC-11 Realization | Study Magic | MagicBoundary, MagicService, Spell |
|
||
| UC-12 Realization | Level Up at Review Board | ReviewBoundary, ReviewBoard, Progression |
|
||
|
||
### 9.2 Analysis View (Use-Case Realizations)
|
||
|
||
Each realization package in the EA model carries a class diagram (boundary/controller/entity with §2a
|
||
associations); renders in this folder, generated by `scripts/openup/export-diagram-images.ps1`:
|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||
### 9.3 Interaction View
|
||
|
||
Design sequences for all twelve use-case realizations. Sources are the version-controlled
|
||
PlantUML files under `sequences/` (working copy per the PlantUML→EA workflow canon; the EA
|
||
model carries scenarios with per-step actors). Render: `java -jar plantuml.jar -tpng
|
||
-charset UTF-8 sequences/*.puml`; lifelines = the realization's analysis classes
|
||
(mapping §2), messages = the actual design elements (routes and domain functions).
|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||
### 9.4 Operational View
|
||
|
||
Single Rust process per deployment (systemd unit on the self-hosted boxes; Forgejo + Jenkins pipeline per
|
||
vision.md). All state in memory, optional write-through SQLite (NFR-01/06/14); no Redis for MVP. The Vue SPA
|
||
is served statically and talks to the API over HTTPS (Caddy, tech-stack canon). Clients poll; no push
|
||
channel in the MVP (WebSocket notifications are a later-phase feature, vision.md).
|
||
|
||
### 9.5 Design Diagram Index
|
||
|
||
| Diagram | File | Documents |
|
||
|---|---|---|
|
||
| Use Case Overview | `../requirements/use-case-overview.png` | actors, use cases, associations |
|
||
| UC-01…UC-12 Realization | `uc-01-realization.png` … `uc-12-realization.png` | analysis classes + associations per UC |
|
||
| UC-01…UC-12 Sequence | `sequences/uc-01-register.png` … `sequences/uc-12-level-up.png` (sources: `sequences/*.puml`) | design interactions per UC |
|
||
| Component Architecture | `component-architecture.png` | joint component model, dependency rule |
|
||
|
||
EA model renders are produced by `scripts/openup/export-diagram-images.ps1`; sequence
|
||
renders by PlantUML from the committed sources. Regenerate after changes (drift rule).
|
||
|
||
## 10. References
|
||
|
||
- `requirements/vision.md`, `requirements/use-case-model.md`, `requirements/supplementary-specifications.md`
|
||
- `analysis-design/uc-realization-mapping.md` (§1 structure, §2 mapping, §2a interactions, §3 component model)
|
||
- `analysis-design/design-wi-1-injectable-rng.md`, `design-wi-2-architecture-cleanup.md` (detailed WI designs)
|
||
- EA model: `docs/openup/model/skara-brae.eapx` (+ `exports/` JSON, `BUILD-CHECKLIST.md`)
|
||
- Client-side UC realizations: future work item (joint-model decision, mapping §1)
|