# 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 | ![Component Architecture](component-architecture.png) *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`: ![UC-01 Realization](uc-01-realization.png) ![UC-02 Realization](uc-02-realization.png) ![UC-03 Realization](uc-03-realization.png) ![UC-04 Realization](uc-04-realization.png) ![UC-05 Realization](uc-05-realization.png) ![UC-06 Realization](uc-06-realization.png) ![UC-07 Realization](uc-07-realization.png) ![UC-08 Realization](uc-08-realization.png) ![UC-09 Realization](uc-09-realization.png) ![UC-10 Realization](uc-10-realization.png) ![UC-11 Realization](uc-11-realization.png) ![UC-12 Realization](uc-12-realization.png) ### 9.3 Interaction View Not yet modeled: no sequence diagrams exist for the Skara Brae MVP (the EA model carries scenarios with per-step actors, not interactions). Candidate first sequences: UC-08 adventure request→combat→loot, and UC-01/02 auth flows. Adding them is a future work item (PlantUML prototype → EA per the workflow canon). ### 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 | | Component Architecture | `component-architecture.png` | joint component model, dependency rule | All three sets are renders of the EA model (`docs/openup/model/skara-brae.eapx`) produced by `scripts/openup/export-diagram-images.ps1`; regenerate after model changes (read-only, layout-preserving). ## 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)