# Architecture Decisions ## Blazor Hosting Model: Server vs WebAssembly ### How They Differ | Aspect | Blazor Server | Blazor WASM | |--------|--------------|-------------| | Code runs | On the server | In the browser | | UI updates | Via SignalR WebSocket (server pushes diffs) | Locally in browser (no round-trip) | | Calling backend services | Direct — code is already server-side | Needs HTTP calls if accessing server resources | | Offline capable | No (requires persistent connection) | Yes | | Startup speed | Fast | Slower (downloads .NET runtime to browser) | ### Decision: Blazor WASM This project uses **Blazor WebAssembly (standalone)** with a separate ASP.NET Core Web API backend. ## When Do You Need a Separate API Project? There are two distinct concerns that are easy to conflate: ### 1. Consuming an External API (e.g. OpenAI) Both Blazor Server and WASM can call external APIs directly from C# — no controller needed. The difference is **where that code executes**: - **Blazor Server**: Code runs on the server. A service class calls OpenAI directly. API keys live safely in server memory. No controller, no exposed endpoint required. - **Blazor WASM**: Code runs in the browser. You *can* call OpenAI directly, but any **API key would be embedded in the browser download** — anyone could inspect it via browser dev tools. This is the primary reason to add a backend proxy. ### 2. Exposing an API Endpoint (e.g. ChatAgent.Api) This is a separate concern. You expose an API endpoint when: - Another application needs to communicate with your chat system - You want a REST API for mobile clients, scripts, or integrations - You need a server-side proxy to protect secrets from the browser (the WASM case above) **Key insight:** You don't need an API endpoint to *use* an external service. You only need one in WASM to **keep secrets out of the browser**. ### 3. Component Updates Do Not Require an API In both hosting models, Blazor components update themselves locally: - Components hold state in fields/properties - Calling `StateHasChanged()` triggers a re-render - Components can call injected C# services directly - No HTTP round-trip is needed for UI updates For example, a chat component that echoes "success msg!" back needs no API at all — a simple injected service handles it entirely within the client project. ## API Key Management Scenarios The need for an API project in WASM depends on how secrets are managed: | Scenario | WASM needs API project? | Why | |----------|------------------------|-----| | Raw API key in config | Yes | To keep the key out of browser-downloadable code | | Azure Key Vault | Yes | Browser sandbox cannot access Key Vault or managed identity | | API Management gateway (Azure APIM) with token auth | No | WASM calls the gateway directly; gateway handles auth via managed identity | | Blazor Server (any scenario) | No | Code is already server-side; secrets never leave the server | ### Enterprise Pattern: API Gateway In enterprise environments, a common pattern avoids the need for a custom API proxy entirely: 1. An **API Management gateway** (e.g. Azure APIM) sits in front of the external service (e.g. OpenAI) 2. The gateway authenticates via managed identity and handles secret retrieval 3. The gateway exposes a public endpoint requiring only a subscription key or OAuth token 4. WASM calls the gateway directly — no secrets in the browser, no custom API project needed The "proxy" becomes infrastructure rather than application code. ## Current Project Structure ``` ChatAgent.sln src/ ChatAgent.Client/ -- Blazor WASM (standalone) Pages/ -- Routable page components Layout/ -- MainLayout, NavMenu Services/ -- Client-side services (e.g. ChatApiClient) Program.cs -- Client entry point, DI registration ChatAgent.Api/ -- ASP.NET Core Web API (backend proxy) Controllers/ -- API controllers (e.g. HealthController) Program.cs -- Server entry point, middleware config ChatAgent.Shared/ -- Models shared between Client and Api Models/ -- DTOs (e.g. HealthResponse) ``` ### Why Three Projects? - **ChatAgent.Client**: The Blazor WASM app running in the browser - **ChatAgent.Api**: Exists to proxy requests that require server-side secrets (e.g. future OpenAI calls). Not needed for basic component interactions. - **ChatAgent.Shared**: Models referenced by both Client and Api, avoiding duplication For the initial "echo success" phase, only ChatAgent.Client is actively used. The Api and Shared projects exist to support future integration with external services that require secret management.