# Skara Brae — Analysis & Design Model (text form) Version 1.0 (WI-2, 2026-10-03) · OpenUP artifact: `analysis-design/uc-realization-mapping.md` This is the **content the EA model will carry** once the modeling pipeline exists. The EA repository itself (`.eapx`, keyfob-station style) is planned for a later work item — EA is a human-gated interactive tool; this document is the authoritative source any future EA builder script generates from. ## 1. Model structure (planned EA packages) ``` Model: Skara Brae ├── Requirements │ ├── Vision (from requirements/vision.md) │ ├── Use Case Model │ │ ├── Actors: Player, Administrator, Guest │ │ └── Use Cases UC-01 … UC-12 (from requirements/use-case-model.md; │ │ scenarios as Basic Path / Alternate / Exception with per-step Actor) │ └── Supplementary Specifications (from requirements/supplementary-specifications.md) └── Analysis-Design ├── Architecture Overview (crate/layer diagram, dependency rule) ├── UC-01 Realization … UC-12 Realization (one package per UC: analysis classes, │ design elements, realization table below) └── Design (component model of the joint architecture — server crates + skara-brae-client + skara-brae-common; §3) ``` Joint-model decision (2026-10-03): one EA model covers the whole product — server, client and common — because the use cases are player-facing and Requirement→realization traceability spans tiers. Client-side UC realization packages (Vue boundary elements per UC) are a future work item: no markdown source exists yet; they are NOT invented here. Today the client enters the model via the §4 component model only. ## 2. UC realization mapping (analysis classes → design elements) Legend — **Analysis class** (OpenUP stereotype): `«controller»`, `«entity»`, `«boundary»`. **Design element**: crate::module::item (Rust). "WI-2" marks elements introduced or re-homed by this work item; everything else exists at 899fde8. | UC | Analysis class | Stereotype | Design element | Notes | |---|---|---|---|---| | UC-01 Register | AccountRegistry | controller | `sb-api::routes::{register, seed_users}` | argon2 hash via `sb-auth::hash`; JWT via `sb-auth::jwt` | | | UserAccount | entity | `sb-shared::user::User`, `Role` | persisted via `sb-db::Database` | | | AuthBoundary | boundary | `POST /api/auth/register`, `AuthResponse` | | | UC-02 Authenticate | Authenticator | controller | `sb-api::routes::login`, `sb-auth::{hash::verify_password, jwt::create_token}` | | | | TokenService | boundary | `POST /api/auth/login`, `AuthResponse`, `middleware::auth::auth_middleware` | middleware guards all UC-03…12 | | UC-03 Manage Characters | CharacterFactory | controller | `sb-core::character::create_character` | takes injected rng (WI-1) | | | Character | entity | `sb-shared::character::Character` (+ `Race`, `Class`, `CharacterStats`) | | | | CharacterCatalog | boundary | `GET/POST /api/users/{id}/characters`, `GET /api/characters/{id}` | store: `AppState.characters` | | | ResourceRegenerator | entity | `sb-core::character::apply_regen` (+ regen constants — WI-2 names them) | included sub-flow | | UC-04 Browse Catalogue | CatalogueBrowser | boundary | `GET /api/{races,classes,items,enemies,adventures}`; response types `sb-shared::responses` | data from `sb-common` JSON via `sb-core::{character::RACES/CLASSES, data, items}` | | | GameData | entity | `sb-core::data::{EnemyDef, ZoneDef, EncounterEntry, LootDrop}`, `items::load_all_items` | | | UC-05 Train Stat | TrainingService | controller | `sb-core::services::train_stat` | error type `DomainError` (WI-2) | | | TrainingGrounds | entity | `sb-core::character::TRAINING_GROUNDS`, `training_cost` (+ cost constants WI-2) | | | | GymBoundary | boundary | `GET/POST /api/characters/{id}/gym`, `GymResponse`, `TrainStatResponse` | | | UC-06 Manage Equipment | EquipmentService | controller | `sb-core::services::{equip_item, unequip_item}` | `DomainError` (WI-2) | | | Equipment/Inventory | entity | `Character.equipment` / `.inventory`, `sb-shared::item::Item` | | | | EquipmentBoundary | boundary | `POST /api/characters/{id}/{equip,unequip}` | | | UC-07 Trade at Shop | ShopService | controller | `sb-api::routes::{shop_action, buy_item, sell_item}` (handler-side; extraction candidate, design note Q3) | prices: `sb-core::items::{item_price, item_sell_price}` | | | ShopStock | entity | `AppState.shop_stock` | persisted via `sb-db` | | | ShopBoundary | boundary | `GET/POST /api/characters/{id}/shop`, `ShopResponse`, `ShopActionResponse` | | | UC-08 Run Adventure | **AdventureService** | controller | `sb-core::adventure::run_adventure` (**new, WI-2**) + `services::pick_enemy` → `Result` (WI-2) | takes injected rng; `DomainError` (WI-2) | | | CombatEngine | entity | `sb-core::combat::{resolve_combat_full, resolve_attack, crit tables, open_ended_roll}` + combat constants (WI-2) | | | | Combatant | entity | `sb-core::combat::CombatStats` | built from Character/EnemyDef | | | SpellEffect / BardSong | entity | `sb-core::combat::SpellEffect`, `sb-core::spells`, `sb-core::songs` | | | | AdventureBoundary | boundary | `GET /api/characters/{id}/adventure`; `AdventureRunResponse` (**new, WI-2**), `sb-shared::responses::CombatLog` | | | UC-09 Heal at Temple | HealingService | controller | `sb-api::routes::heal_character` (handler-side; extraction candidate, Q3) | | | | TempleBoundary | boundary | `POST /api/characters/{id}/heal`; `HealResponse` (**new, WI-2**) | | | UC-10 Practice Bard Song | SongService | controller | `sb-api::routes::set_song`; `sb-core::songs::available_songs` | | | | BardSong | entity | `sb-core::songs::BARD_SONGS`, `Character.active_song` | | | | SongBoundary | boundary | `GET/POST /api/characters/{id}/songs`, `SongsResponse`, `SetSongResponse` | | | UC-11 Study Magic | MagicService | controller | `sb-api::routes::{get_spells, learn_spell_level}`; `sb-core::spells::{spells_for_class, spell_learn_cost, max_spell_points, caster_spell_level (WI-2)}` | | | | Spell | entity | `sb-core::spells::SpellDef`, `Character.{known_spells, active_spell, spell_points}` | | | | MagicBoundary | boundary | `GET/POST /api/characters/{id}/spells`; `SpellbookResponse`, `LearnSpellResponse` (**new, WI-2**) | | | UC-12 Level Up | ReviewBoard | controller | `sb-core::character::level_up` | `DomainError` (WI-2) | | | Progression | entity | `sb-core::character::{xp_for_level, level_up_cost, MAX_LEVEL}` (+ curve constants WI-2) | | | | ReviewBoundary | boundary | `POST /api/characters/{id}/level-up`, `LevelUpResponse` | | Cross-cutting design elements (WI-2): `sb-core::error::DomainError` + `sb-api::routes::domain_status` (status mapping table, design note §4.1) realize the error paths of UC-05/06/08/12; `sb-core::constants` realizes NFR "no magic numbers"; `sb-api::routes::responses` realizes NFR-19 (wire-format freeze). Dependency rule check (Clean Architecture): all controllers/entities above live in `sb-core`/`sb-shared` (domain side, no HTTP imports); all boundaries live in `sb-api`; the single current violations (ShopService, HealingService, parts of MagicService in sb-api) are recorded as debt with a follow-up question — the model must not paper over them (stale docs are defects). ### 2a. Analysis interactions (associations + dependencies, added 2026-10-03) Within each realization package the analysis classes collaborate per the robustness rule boundary ↔ controller ↔ entity (boundary never touches entity directly — exception: UC-04 has no controller, its boundary reads the static catalogue entity). Each association serves the UC's Basic Path steps (boundary receives the actor request, controller applies the rules, entities hold the state). Cross-cutting: the four `«uses»` dependencies realize the error paths (Alternate/Exception flows) named in the cross-cutting paragraph above. | UC | From | To | Type | Serves | |---|---|---|---|---| | UC-01 | AuthBoundary | AccountRegistry | Association | MSS steps 1–4 | | UC-01 | AccountRegistry | UserAccount | Association | MSS steps 3–4 | | UC-02 | TokenService | Authenticator | Association | MSS steps 1–3 | | UC-03 | CharacterCatalog | CharacterFactory | Association | MSS (create) steps 1–4 | | UC-03 | CharacterFactory | Character | Association | MSS steps 3–4 | | UC-03 | CharacterFactory | ResourceRegenerator | Association | list/view (regen on return) | | UC-04 | CatalogueBrowser | GameData | Association (no controller in this realization) | MSS steps 1–2 | | UC-05 | GymBoundary | TrainingService | Association | MSS steps 1–5 | | UC-05 | TrainingService | TrainingGrounds | Association | MSS steps 3–4 | | UC-06 | EquipmentBoundary | EquipmentService | Association | MSS (equip) + unequip | | UC-06 | EquipmentService | Equipment/Inventory | Association | MSS steps 2–3 | | UC-07 | ShopBoundary | ShopService | Association | MSS (buy) + sell | | UC-07 | ShopService | ShopStock | Association | MSS steps 3–4 | | UC-08 | AdventureBoundary | AdventureService | Association | MSS steps 1–9 | | UC-08 | AdventureService | CombatEngine | Association | MSS step 5 | | UC-08 | AdventureService | Combatant | Association | MSS step 5 | | UC-08 | AdventureService | SpellEffect / BardSong | Association | MSS steps 5, 8 | | UC-09 | TempleBoundary | HealingService | Association | MSS steps 1–3 | | UC-10 | SongBoundary | SongService | Association | MSS steps 1–2 + clear | | UC-10 | SongService | BardSong | Association | MSS steps 1–2 | | UC-11 | MagicBoundary | MagicService | Association | view + learn + activate | | UC-11 | MagicService | Spell | Association | learn + activate steps | | UC-12 | ReviewBoundary | ReviewBoard | Association | MSS steps 1–3 | | UC-12 | ReviewBoard | Progression | Association | MSS step 2 | The authoritative connector count is **24 associations** (UC-01:2, UC-02:1, UC-03:3, UC-04:1, UC-05:2, UC-06:2, UC-07:2, UC-08:4 — its realization has one boundary, one controller and three entities, UC-09:1, UC-10:2, UC-11:2, UC-12:2) plus **4 cross-cutting dependencies** below — 28 connectors in total. | UC | From (controller) | To | Type | Serves | |---|---|---|---|---| | UC-05 | TrainingService | DomainError (Architecture Overview) | Dependency «uses» | Alternate flows (error paths) | | UC-06 | EquipmentService | DomainError (Architecture Overview) | Dependency «uses» | Alternate flows (error paths) | | UC-08 | AdventureService | DomainError (Architecture Overview) | Dependency «uses» | Exception (unknown enemy → 500) | | UC-12 | ReviewBoard | DomainError (Architecture Overview) | Dependency «uses» | Alternate flows (error paths) | ## 3. Design component model (joint architecture, added 2026-10-03) Package `Analysis-Design / Design` — components of the three repos and their dependency rule (sources: design note §2–3, mapping §2 dependency paragraph, client repo @ 34dc02f, common repo @ wi-2-openup-docs). Every component note carries the UCs it serves. | Component | Kind | Notes (traceability) | |---|---|---| | skara-brae-client | Component (presentation tier) | Vue 3.5 SPA (TypeScript, Pinia, Vite); monolithic App.vue @ 34dc02f; JWT bearer; REST via Vite proxy to :8080. Serves UC-01…12 as the player-facing boundary. | | sb-api | Component (boundary/adapter) | Axum routes/handlers, `domain_status`, `responses` (wire-format freeze). May import sb-core, sb-shared, sb-common, sb-db, sb-auth (design §3). Serves all UC boundaries per §2. | | sb-auth | Component (adapter) | argon2 hashing + JWT (UC-01/02). Imports sb-shared. | | sb-db | Component (adapter) | persistence via `sb-db::Database`. Imports sb-shared. | | sb-core | Component (domain) | controllers/entities: adventure (UC-08), services (UC-05/06), character (UC-03/12), combat, spells (UC-11), songs (UC-10), data/items (UC-04). Must not reference axum/http/serde_json::json!/tokio/sb-db/sb-api (design §3). | | sb-shared | Component (domain types) | `User`, `Character`, `Item`, response types. Zero HTTP imports. Entities of all UCs. | | sb-common | Component (data) | bilingual data package: data/ JSON (items, enemies, spells, zones), rust/ crate, ts/ package, codegen/. UC-04 data source; consumed by sb-core + sb-api; future client codegen. | Dependencies (arrows = depends on): skara-brae-client → sb-api (REST/JSON); sb-api → {sb-core, sb-shared, sb-db, sb-auth, sb-common}; sb-core → {sb-shared, sb-common}; sb-db → sb-shared; sb-auth → sb-shared. The dependency rule: inward toward sb-core/sb-shared (domain); adapters (sb-api/sb-db/sb-auth) and the client at the edge. ![Component Architecture](component-architecture.png) *Figure: joint component architecture (rendered from the EA model via `scripts/openup/export-diagram-images.ps1`). Per-UC realization figures: `uc-01-realization.png` … `uc-12-realization.png` in this folder; they are embedded in the Architecture Notebook (§9.2).* ## 4. Research summary — keyfob-station EA pipeline (reference) Source: read-only clone `http://192.168.1.10:3000/jan/keyfob-station.git` (inspected 2026-10-03; no modifications). - **Model file:** `docs/openup/model/keyfob-station.eapx` (MS Access database — the EA native format; EABase-derived). Never hand-edited. - **Structure:** Model → `Requirements` package → `Use Case Model` package containing `Actor` and `UseCase` elements; scenario lists are stored on each use case as named scenarios of type **Basic Path / Alternate / Exception**, each with ordered steps carrying a per-step **Actor** tag and notes. An `Analysis-Design` package holds **one `UC-N Realization` package per use case** (realization diagrams + design elements). - **Builders are surgical scripts:** `scripts/add-*.ps1` (PowerShell driving the EA COM object `New-Object -ComObject EA.App`, `$repo.OpenFile($modelPath)`). Each script opens the model, adds/rebuilds exactly one package's contents, and closes it — because EA repository IDs and package rebuilds clobber manual edits, hand edits do not survive; all content lives in the scripts (scenario text is embedded as PowerShell arrays of `Name/Type/Notes/Steps`). This matches our standards note: *model additions are surgical; builders rebuild whole packages*. - **Exports:** `scripts/export-model-json.ps1` walks the Use Case Model package and emits `exports/use-case-model.json` + `supplementary-requirements.json`; realization diagrams are exported to PDF/PNG into `docs/openup/analysis-design/`. The JSON export is the doc-machine readable form of the same content. - **Windows-only:** the pipeline runs on Windows (EA is a Win32 app; scripts use `.ps1` + COM). For Skara Brae, the same pattern applies when the modeling WI lands: keep `docs/openup/*.md` (this file set) as the source the builder scripts embed, never the reverse. Adoption decision for Skara Brae (updated at 1B rework): the EA model + generator pipeline is **in WI-2 scope**. This document (§1–2) plus `requirements/use-case-model.md` are the content source; the model-home recommendation, agent preparation steps, generator scripts (`scripts/openup/build-model.ps1`, `export-model.ps1`), and the human-gate checklist are specified in `design-wi-2-architecture-cleanup.md` §11. The `.eapx` interactive editing session remains human-gated; everything around it is COD-phase agent work.