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
16 KiB
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-sharedwith 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 viaSB_DB_PATH(NFR-01, NFR-14). - Static game data ships with the binary via
include_str!fromskara-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-commonis 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 importsb-core,sb-shared,sb-common,sb-db,sb-auth;sb-coremust 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 mappingsb-api::routes::domain_statusrealize the error paths of UC-05/06/08/12 (design note §4.1). - D3 — Constants centralized.
sb_core::constantsis 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;
AdventureOutcomederives nothing (design note §2 placement decisions). - D5 — Injectable RNG with a single construction point.
request_rng()(StdRng::from_os_rng, WI-1); domain functions takerng: &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.ps1rebuilds 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.mdanalysis-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)
























