Architecture Overview
The Tech Strategy Tool is built on an event-sourced architecture with a single API host serving two Blazor WebAssembly frontends. This page describes the high-level system design and the reasoning behind key architectural decisions.
System Architecture
Section titled “System Architecture”All GET requests are served from the event processor’s in-memory state — no database queries for current data. All writes are serialized through the processor via a semaphore lock, then persisted to PostgreSQL. SSE notifications flow back to all connected clients so they can re-fetch affected entities.
Design Principles
Section titled “Design Principles”Event sourcing as a natural fit
Section titled “Event sourcing as a natural fit”Event sourcing was chosen because history, restore, and conflict detection are first-class product features — not infrastructure afterthoughts.
-
History is a product feature. The per-entity history view, the re-apply mechanism, and full-strategy restore are core user-facing capabilities. In a CRUD system, these would require a bolted-on audit log, diff tracking, and snapshot/restore logic. With event sourcing, the event log IS the history, previous values are derived by replaying to any point, and restore is “rebuild the document at sequence N and make it current.”
-
Conflict detection for collaborative editing. The per-field sequence tracking (
lastSeenSequence) that enables safe concurrent editing is a natural extension of the sequential event model. Each field knows which event last modified it, and stale edits are rejected with the current server value. -
The domain is naturally event-shaped. Strategy changes are discrete, attributable actions: “Alice renamed a principle,” “Bob assigned a principle,” “Carol created an initiative.” These map directly to events. Cascading operations (like deleting a principle referenced by multiple objectives) are recorded as a single event with the processor handling all side effects — the event captures intent, the processor handles mechanics.
See Event Sourcing for the full event flow and processing model.
Simplicity at the right scale
Section titled “Simplicity at the right scale”The system targets approximately 20 concurrent editors and 50 viewers. The architecture embraces this constraint:
- A single in-memory document — the entire strategy is held in memory by the event processor
- A single-writer processor — all events are serialized through a
SemaphoreSlim(1,1)lock, eliminating distributed locking and transaction coordination - Deterministic event ordering — a single monotonic sequence number for all events
- Fast reads — all GET requests served from in-memory state, no database round-trip
The trade-off is that the system runs as a single API server instance. This is a deliberate design choice — the tool is correct and simple at this scale, and complexity would only be added when a concrete need materializes.
Two frontends, one host
Section titled “Two frontends, one host”The Strategy Site and Admin Site are separate Blazor WebAssembly applications with distinct concerns:
| Site | URL | Audience | Purpose |
|---|---|---|---|
| Strategy Site | /strategy | Viewers, Editors | Day-to-day editing of principles, objectives, initiatives |
| Admin Site | /admin | Administrators | User management, team setup, event log, restore |
Both are served by the same ASP.NET Core host and share the same API, cookie-based authentication, and SSE infrastructure. Keeping them separate means:
- The Strategy Site is optimized for the editing workflow (low friction, meeting-friendly)
- The Admin Site is a management console with different interaction patterns
- The Strategy Site does not ship admin UI code to non-admin users
- Both apps can evolve independently
They share a common foundation:
Real-time updates via SSE
Section titled “Real-time updates via SSE”Server-Sent Events push lightweight notifications to all connected clients. When an event is processed, the server broadcasts which entities were affected — entity type and ID only, no payloads. Clients then fetch fresh data for only the changed entities.
This approach:
- Avoids pushing full document state on every change
- Keeps SSE messages minimal (no data synchronization complexity)
- Lets clients decide how to refresh (single card vs. full view reload)
- Uses the browser’s native
EventSourceAPI with automatic reconnection
See Real-Time Updates (SSE) for protocol details.
Request Flow
Section titled “Request Flow”A typical user action follows this path:
- The Blazor client submits an event via
POST /api/events - The API acquires the single-writer lock
- The event processor validates and applies the change in memory
- The resulting event(s) are persisted to PostgreSQL
- SSE notifications are broadcast to all connected clients (including the submitter)
- The submitting client receives the processing result
- All clients receive SSE notifications and re-fetch the affected entity from the API
- The API serves GET requests from the processor’s in-memory state (no database query)
Cold Start and Recovery
Section titled “Cold Start and Recovery”On startup, the server reconstructs its in-memory state before accepting any requests:
- Load the latest checkpoint from the database (a serialized snapshot of the strategy document)
- Replay all events after the checkpoint’s sequence number
- Save a new checkpoint (ensures old non-decomposed events are never replayed again)
- Begin accepting requests
This process is handled by ProcessorInitializer, an IHostedService that runs before the app starts serving traffic.
Key Files
Section titled “Key Files”| File | Purpose |
|---|---|
src/TechStrat.Core/Processing/EventProcessor.cs | Core event processing loop |
src/TechStrat.Api/Endpoints/EventEndpoints.cs | Event submission endpoint with single-writer lock |
src/TechStrat.Api/Sse/SseConnectionManager.cs | SSE connection tracking and broadcast |
src/TechStrat.Infrastructure/Persistence/EventStore.cs | Event persistence |
src/TechStrat.Api/Auth/SessionAuthenticationHandler.cs | Cookie-based session auth |
Further Reading
Section titled “Further Reading”- Event Sourcing — Event flow, checkpointing, replay, and decomposition
- Real-Time Updates (SSE) — SSE protocol, heartbeat, and reconnection
- Authentication & Security — Session management, CSRF, rate limiting
- Data Model — Entity hierarchy and relationships