Update der Dokumentation
This commit is contained in:
@@ -1,13 +1,19 @@
|
||||
---
|
||||
type: Architecture
|
||||
title: System Architecture & Security Overview
|
||||
description: Technical architecture of SlipItIn, covering .NET Aspire orchestration, ASP.NET Core SignalR hub design, JWT authentication, and IDbContextFactory thread safety.
|
||||
tags: [architecture, spire, signalr, jwt, efcore, security]
|
||||
description: Technical architecture of SlipItIn, covering .NET Aspire orchestration, ASP.NET Core SignalR hub, .NET MAUI MVVM client layer, WeakReferenceMessenger event routing, and dual-layer local storage.
|
||||
tags: [architecture, spire, signalr, jwt, efcore, security, maui, mvvm]
|
||||
openwiki:
|
||||
roles: [architecture, domain]
|
||||
change_kinds: [lifecycle, public-api]
|
||||
source_paths: [SlipItIn/MauiProgram.cs, SlipItIn/Services/SignalRService.cs, SlipItIn/Services/GameStateService.cs, SlipItIn.Server/Program.cs, SlipItIn.Server/Hubs/GameHub.cs]
|
||||
symbols: [SignalRService, GameStateService, LocalStorageService, GameHub, GameService, IDbContextFactory]
|
||||
validation_commands: ["dotnet build SlipItIn.slnx"]
|
||||
---
|
||||
|
||||
# System Architecture & Security Overview
|
||||
|
||||
**SlipItIn** is designed as a distributed, real-time application using modern .NET 10 architecture. This document details the orchestration model, backend service structure, security model, and concurrency safeguards.
|
||||
**SlipItIn** is designed as a distributed, real-time application using modern .NET 10 architecture. This document details the orchestration model, backend server structure, .NET MAUI MVVM client architecture, real-time connection resilience, messaging infrastructure, security model, and concurrency safeguards.
|
||||
|
||||
The architectural foundation [orchestrates services with](/openwiki/operations/runbook.md) .NET Aspire, while [enforcing security & privacy on](/openwiki/domain/game-mechanics.md) domain entities and [serving real-time hub endpoints for](/openwiki/workflows/slip-and-challenge.md) all active game sessions. Source code structure for all architectural components can be found in the [Source Code Map](/openwiki/source-map.md).
|
||||
|
||||
@@ -32,14 +38,14 @@ The solution uses **.NET Aspire** to orchestrate application resources, services
|
||||
|
||||
The backend is an ASP.NET Core Web API & SignalR application providing REST endpoints for user management and real-time WebSockets for game state synchronization.
|
||||
|
||||
### Key Components
|
||||
### Key Backend Components
|
||||
|
||||
1. **`Program.cs`**:
|
||||
- Registers `AddServiceDefaults()`, `AddNpgsqlDataSource("postgresdb")`, and `AddDbContextFactory<SlipItInDbContext>()`.
|
||||
- Configures JWT Bearer authentication with custom `OnMessageReceived` token resolution for SignalR.
|
||||
- Automatically executes database migrations (`db.Database.Migrate()`) at startup.
|
||||
2. **`AuthController.cs`**:
|
||||
- Manages user registration (`/api/auth/register`) and authentication (`/api/auth/login`).
|
||||
- Manages user registration (`/api/auth/register`), authentication (`/api/auth/login`), and profile retrieval (`/api/auth/me`).
|
||||
- Uses `BCrypt.Net` for secure password hashing.
|
||||
- Issues JWT tokens signed with `Jwt:Key`, containing `ClaimTypes.NameIdentifier`, `ClaimTypes.Name`, and `ClaimTypes.Email`.
|
||||
3. **`GameHub.cs`**:
|
||||
@@ -48,6 +54,7 @@ The backend is an ASP.NET Core Web API & SignalR application providing REST endp
|
||||
- Extracts and verifies user claims, delegating state mutations to `IGameService`.
|
||||
4. **`GameService.cs`**:
|
||||
- Implements core business logic: game creation, player joins, card dealing, slip submission, challenge creation, and resolution.
|
||||
- Adds `hostPlayer` directly to `game.Players` before `context.Games.Add(game)` to ensure EF Core navigation tracking consistency.
|
||||
|
||||
---
|
||||
|
||||
@@ -101,13 +108,103 @@ sequenceDiagram
|
||||
Client->>Hub: JoinLobby(lobbyCode)
|
||||
Hub->>Hub: GetAuthenticatedUserId()
|
||||
Hub->>Service: JoinGameAsync(lobbyCode, userId, connectionId)
|
||||
Service->>DB: Create IDbContext Session & Save Player
|
||||
Hub-->>Client: Broadcast "PlayerJoined" (GameStateDto)
|
||||
```
|
||||
*Sequence diagram showing user authentication via REST and subsequent authenticated SignalR WebSocket connection.*
|
||||
|
||||
---
|
||||
|
||||
## 4. Concurrency Safety: `IDbContextFactory`
|
||||
## 4. .NET MAUI Client Architecture (`SlipItIn`)
|
||||
|
||||
The cross-platform client app uses the **MVVM Pattern** built on `CommunityToolkit.Mvvm` and Dependency Injection configured in `MauiProgram.cs`.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph UI_Layer["UI Layer (Views)"]
|
||||
LoginPage["LoginPage"]
|
||||
RegisterPage["RegisterPage"]
|
||||
LobbyPage["LobbyPage"]
|
||||
GameBoardPage["GameBoardPage"]
|
||||
end
|
||||
|
||||
subgraph ViewModel_Layer["ViewModel Layer"]
|
||||
LoginVM["LoginViewModel"]
|
||||
RegisterVM["RegisterViewModel"]
|
||||
LobbyVM["LobbyViewModel"]
|
||||
GameBoardVM["GameBoardViewModel"]
|
||||
end
|
||||
|
||||
subgraph Messaging_Layer["Messaging Layer"]
|
||||
Messenger["WeakReferenceMessenger"]
|
||||
Messages["Messages (GameStateChanged, PlayerHandChanged, ChallengeReceived)"]
|
||||
end
|
||||
|
||||
subgraph Service_Layer["Service Layer"]
|
||||
GameStateSvc["GameStateService"]
|
||||
SignalRSvc["SignalRService"]
|
||||
AuthSessionSvc["AuthSessionService"]
|
||||
ApiSvc["ApiService"]
|
||||
LocalStorageSvc["LocalStorageService"]
|
||||
end
|
||||
|
||||
LoginPage --> LoginVM
|
||||
RegisterPage --> RegisterVM
|
||||
LobbyPage --> LobbyVM
|
||||
GameBoardPage --> GameBoardVM
|
||||
|
||||
LobbyVM --> Messenger
|
||||
GameBoardVM --> Messenger
|
||||
|
||||
GameStateSvc --> Messenger
|
||||
GameStateSvc --> SignalRSvc
|
||||
GameStateSvc --> LocalStorageSvc
|
||||
LoginVM --> ApiSvc
|
||||
LoginVM --> AuthSessionSvc
|
||||
LoginVM --> SignalRSvc
|
||||
```
|
||||
*Layered architecture diagram of the .NET MAUI client showing Views, ViewModels, WeakReferenceMessenger, and Service implementations.*
|
||||
|
||||
### Dependency Injection & Service Registration (`MauiProgram.cs`)
|
||||
- **Singletons**: `IAppConfigurationService` (`AppConfigurationService`), `ILocalStorageService` (`LocalStorageService`), `IAuthSessionService` (`AuthSessionService`), `IApiService` (`ApiService`), `ISignalRService` (`SignalRService`), `IGameStateService` (`GameStateService`), `LobbyViewModel`, `GameBoardViewModel`.
|
||||
- **Transients**: `LoginViewModel`, `RegisterViewModel`.
|
||||
- **Service Locator Helper**: `ServiceHelper.Services` captures `app.Services` at startup to allow static access where constructor DI is unavailable.
|
||||
|
||||
### Decoupled Messaging Infrastructure (`WeakReferenceMessenger`)
|
||||
To avoid memory leaks caused by long-lived event subscriptions in ViewModels, `GameStateService` translates raw `ISignalRService` hub events into strongly-typed messages dispatched via `WeakReferenceMessenger.Default`:
|
||||
|
||||
- `LobbyCreatedMessage`: Dispatched when a new game lobby code is issued by the server.
|
||||
- `GameStateChangedMessage`: Dispatched when public game state or player list updates.
|
||||
- `GameStartedMessage`: Dispatched when the game transitions to `InProgress`.
|
||||
- `PlayerHandChangedMessage`: Dispatched when a player receives or updates private phrase cards.
|
||||
- `ChallengeReceivedMessage`: Dispatched when a player is directly targeted by a slip challenge.
|
||||
- `ErrorOccurredMessage`: Dispatched when a hub or network error occurs.
|
||||
- `ConnectionStateChangedMessage`: Dispatched when SignalR connection drops or reconnects.
|
||||
|
||||
ViewModels implement `IRecipient<TMessage>` and register via `WeakReferenceMessenger.Default.RegisterAll(this)` to receive automated UI updates.
|
||||
|
||||
### Real-Time Connection Resilience & Auto-Reconnect
|
||||
`SignalRService` configures exponential backoff for WebSocket reconnection:
|
||||
|
||||
```csharp
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(_configuration.HubUrl, options =>
|
||||
{
|
||||
options.AccessTokenProvider = () => Task.FromResult<string?>(token);
|
||||
})
|
||||
.WithAutomaticReconnect([TimeSpan.Zero, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30)])
|
||||
.Build();
|
||||
```
|
||||
|
||||
When connection drops, `SignalRService` emits `ConnectionStateChanged`. Upon reconnection, `GameStateService` automatically triggers `ResyncAsync()` to fetch current state via `RequestGameStateAsync()` and flush locally queued offline actions.
|
||||
|
||||
### Dual-Layer Storage Security Model (`LocalStorageService`)
|
||||
To satisfy security and privacy constraints:
|
||||
1. **`SecureStorage` (Platform Encrypted Storage)**: Used strictly for `AuthTokenKey` (`auth_token`). Prevents plaintext access to active JWT credentials.
|
||||
2. **`Preferences` (Application Key-Value Settings)**: Used for non-sensitive cached data (`auth_user`, `game_state`, `player_hand`, and `queued_actions`).
|
||||
|
||||
---
|
||||
|
||||
## 5. Concurrency Safety: `IDbContextFactory`
|
||||
|
||||
SignalR hubs process concurrent requests from multiple clients over persistent connections. Using a standard Scoped `DbContext` in SignalR leads to thread conflict exceptions (`InvalidOperationException: A second operation was started on this context instance before a previous operation completed`).
|
||||
|
||||
@@ -119,7 +216,7 @@ SlipItIn solves this by registering EF Core with `AddDbContextFactory<SlipItInDb
|
||||
|
||||
---
|
||||
|
||||
## 5. Data Privacy Isolation Model
|
||||
## 6. Data Privacy Isolation Model
|
||||
|
||||
To prevent players from inspecting opponent cards via network trace analysis, SlipItIn strictly separates public and private data DTOs:
|
||||
|
||||
@@ -127,3 +224,30 @@ To prevent players from inspecting opponent cards via network trace analysis, Sl
|
||||
* **`PlayerHandDto` (Private)**: Direct unicast message sent *only* to the specific player's SignalR `ConnectionId`. Contains card text (`CardId`, `PhraseId`, `Text`, `IsUsed`).
|
||||
|
||||
This architecture guarantees that players cannot see opponent phrase cards, even if they inspect raw WebSocket packets.
|
||||
|
||||
---
|
||||
|
||||
## 7. Guidance for Future Agents & Developers
|
||||
|
||||
When extending or modifying the system architecture:
|
||||
|
||||
- **When to Consult**: Consult this document before adding new real-time SignalR methods, introducing client ViewModels, modifying storage mechanisms, or altering server authentication logic.
|
||||
- **Runtime Invariants**:
|
||||
- `GameHub` methods must validate caller claims via `GetAuthenticatedUserId()` and `ValidatePlayerAccessAsync()`.
|
||||
- Client ViewModels must consume `WeakReferenceMessenger` messages rather than subscribing directly to `ISignalRService` C# events.
|
||||
- JWT tokens must never be persisted in `Preferences`; always use `SecureStorage`.
|
||||
- **Extension Points**:
|
||||
- New real-time server messages: Add method to `GameHub`, add event to `ISignalRService`, add message class in `SlipItIn/Messages/`, update `GameStateService` listener.
|
||||
- New ViewModels: Register as Transient (dialogs/auth) or Singleton (main navigation screens) in `MauiProgram.cs`.
|
||||
- **Primary Source Files**:
|
||||
- `SlipItIn/MauiProgram.cs`
|
||||
- `SlipItIn/Services/SignalRService.cs`
|
||||
- `SlipItIn/Services/GameStateService.cs`
|
||||
- `SlipItIn/Services/LocalStorageService.cs`
|
||||
- `SlipItIn.Server/Hubs/GameHub.cs`
|
||||
- `SlipItIn.Server/Services/GameService.cs`
|
||||
- **Focused Checks**: Build and verify project references across client, server, and shared libraries.
|
||||
- **Minimal Validation Command**:
|
||||
```bash
|
||||
dotnet build SlipItIn.slnx
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user