Add TECH kickoff blueprint from current architecture
Some checks failed
CI / build-and-test (push) Has been cancelled
Some checks failed
CI / build-and-test (push) Has been cancelled
This commit is contained in:
319
TECH.md
Normal file
319
TECH.md
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
# TECH - Kickoff Blueprint from Pick'n'Play
|
||||||
|
|
||||||
|
This document extracts the implementation patterns, conventions, and guardrails from the current codebase so a new webapp can start with proven structure and avoid known pitfalls.
|
||||||
|
|
||||||
|
Scope covered:
|
||||||
|
- Backend (`Program.cs`, `Endpoints/*`, `Infrastructure/*`, `Data/*`, `Domain/*`, `Contracts/*`)
|
||||||
|
- Frontend (`wwwroot/*`)
|
||||||
|
- Tooling and CI (`scripts/*`, `.github/workflows/ci.yml`, npm tooling)
|
||||||
|
- Tests (`GameList.Tests/*`)
|
||||||
|
- Review carryover (`REVIEW.md` unresolved topics)
|
||||||
|
|
||||||
|
## 1) Stack and baseline choices
|
||||||
|
|
||||||
|
- ASP.NET Core Minimal API on .NET 10.
|
||||||
|
- EF Core + SQLite in current project (single-node deployment).
|
||||||
|
- Cookie authentication (`HttpOnly`, `SameSite=Strict`, secure in production).
|
||||||
|
- Static frontend (HTML/CSS/JS modules), no frontend framework.
|
||||||
|
- OpenAPI generated from backend and consumed by generated JS client.
|
||||||
|
- xUnit integration-heavy test suite with in-memory SQLite and coverage gates.
|
||||||
|
|
||||||
|
## 2) Architecture patterns to keep
|
||||||
|
|
||||||
|
### 2.1 API shape and layering
|
||||||
|
|
||||||
|
- Route mapping in thin endpoint modules (`MapXEndpoints` per feature area).
|
||||||
|
- Domain logic in workflow services (`*WorkflowService`) instead of endpoint lambdas.
|
||||||
|
- Service responses normalized via `ServiceResult<T>` + `ServiceError`, then mapped to HTTP at the edge.
|
||||||
|
- Consistent `ProblemDetails` payloads with `error` extension for machine-usable errors.
|
||||||
|
- Endpoint-level concerns handled by endpoint filters (`AdminOnlyFilter`, `PhaseRequirementFilter`, `PhaseOrJokerFilter`).
|
||||||
|
|
||||||
|
Keep this split:
|
||||||
|
- Endpoint adapters: auth, deserialization, HTTP mapping only.
|
||||||
|
- Workflow services: validation, query/update rules, transactions.
|
||||||
|
- Helpers: shared utility and security-sensitive routines.
|
||||||
|
|
||||||
|
### 2.2 Middleware pipeline discipline
|
||||||
|
|
||||||
|
- Security and behavior depend on middleware ordering; keep explicit ordering.
|
||||||
|
- Important current order:
|
||||||
|
1. Forwarded headers
|
||||||
|
2. Rate limiter
|
||||||
|
3. HSTS + HTTPS redirect (prod)
|
||||||
|
4. Security headers writer
|
||||||
|
5. Base path
|
||||||
|
6. Global exception handling
|
||||||
|
7. Authentication
|
||||||
|
8. Ensure player still exists
|
||||||
|
9. CSRF origin/referer checks
|
||||||
|
10. Authorization
|
||||||
|
11. State change notifier middleware
|
||||||
|
12. Static files
|
||||||
|
13. Endpoint mapping
|
||||||
|
|
||||||
|
### 2.3 State synchronization
|
||||||
|
|
||||||
|
- Event-driven invalidation with SSE (`/api/events/state`) plus heartbeats.
|
||||||
|
- Conditional reads for state (`ETag` + `If-None-Match`) to return `304`.
|
||||||
|
- In-process notifier (`StateChangeNotifier`) with monotonic version and etag stamp.
|
||||||
|
- Mutation middleware (`StateChangeNotificationMiddleware`) emits invalidation only for successful mutating API calls.
|
||||||
|
|
||||||
|
This pattern is a strong baseline for low to medium scale and should be the default in the new app.
|
||||||
|
|
||||||
|
### 2.4 Security baseline
|
||||||
|
|
||||||
|
- Cookie auth with short/medium session sliding expiration plus absolute lifetime cap.
|
||||||
|
- Explicit same-origin CSRF checks for authenticated mutating API calls.
|
||||||
|
- Rate limiting on auth-sensitive and admin-sensitive surfaces with custom `429` payload.
|
||||||
|
- Security headers on all responses (`CSP`, `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, `Permissions-Policy`).
|
||||||
|
- Forwarded headers restricted to configured trusted proxies/networks only.
|
||||||
|
- Owner/admin protection rules enforced in business logic and DB constraints.
|
||||||
|
- Destructive admin operations require password re-confirmation.
|
||||||
|
- Password hashing is versioned and supports transparent upgrade on successful auth.
|
||||||
|
- Current hash defaults to Argon2id, with legacy compatibility retained.
|
||||||
|
|
||||||
|
### 2.5 Data and invariants
|
||||||
|
|
||||||
|
- Strong DB model with explicit constraints:
|
||||||
|
- Unique normalized username
|
||||||
|
- Unique owner partial index
|
||||||
|
- Unique vote key `(PlayerId, SuggestionId)`
|
||||||
|
- Seeded singleton app state row
|
||||||
|
- DB-level guardrails (trigger) complement app-level checks:
|
||||||
|
- Suggestion limit enforced in DB (`suggestion_limit_exceeded`) to survive concurrent writes.
|
||||||
|
- EF patterns:
|
||||||
|
- `AsNoTracking()` for read-only queries
|
||||||
|
- `ExecuteUpdateAsync` / `ExecuteDeleteAsync` for efficient bulk operations
|
||||||
|
- Explicit transactions for multi-step destructive/admin operations
|
||||||
|
- Conflict handling around unique constraints
|
||||||
|
|
||||||
|
### 2.6 Workflow and permission model
|
||||||
|
|
||||||
|
- Phases (`Suggest`, `Vote`, `Results`) drive endpoint access and UX behavior.
|
||||||
|
- Effective phase can be derived from persisted phase + global `resultsOpen`.
|
||||||
|
- Reconciliation helper functions centralize phase alignment rules.
|
||||||
|
- Admin abilities are intentionally constrained per operation (e.g., only specific transitions allowed).
|
||||||
|
|
||||||
|
Keep centralization of workflow rules. Avoid spreading phase/permission checks inline.
|
||||||
|
|
||||||
|
### 2.7 Frontend architecture
|
||||||
|
|
||||||
|
- ES module split by concern:
|
||||||
|
- API wrapper
|
||||||
|
- Data loaders
|
||||||
|
- UI composition
|
||||||
|
- Feature-specific renderers/handlers
|
||||||
|
- Shared utils and runtime dependency injection
|
||||||
|
- Single runtime state object with deliberate clear/reset logic.
|
||||||
|
- Refresh scheduler:
|
||||||
|
- Serialized refreshes (no overlap)
|
||||||
|
- Adaptive polling backoff
|
||||||
|
- SSE-triggered immediate refresh for state mutations
|
||||||
|
- Visibility-aware refresh suppression
|
||||||
|
- API client is generated from OpenAPI operation ids, not handwritten endpoints.
|
||||||
|
- Internationalization:
|
||||||
|
- translation file validation at startup
|
||||||
|
- language-specific FAQ markdown loading with fallback to default language
|
||||||
|
|
||||||
|
### 2.8 Frontend safety and rendering hygiene
|
||||||
|
|
||||||
|
- Safe rendering helpers:
|
||||||
|
- `escapeHtml` for template interpolation
|
||||||
|
- `safeUrl` for links/media
|
||||||
|
- Sensitive modal content set via `textContent`, not interpolated HTML.
|
||||||
|
- Trusted output patterns covered by regression tests.
|
||||||
|
|
||||||
|
Maintain this as a non-negotiable standard for any user-supplied content path.
|
||||||
|
|
||||||
|
### 2.9 Testing strategy patterns
|
||||||
|
|
||||||
|
- Full-stack integration tests via `WebApplicationFactory`.
|
||||||
|
- Real migrations applied to in-memory SQLite during test host startup.
|
||||||
|
- HTTP side effects mocked deterministically (`StubHttpMessageHandler` and `IHttpClientFactory` replacement).
|
||||||
|
- Coverage-focused tests for:
|
||||||
|
- auth/security rules
|
||||||
|
- middleware behavior
|
||||||
|
- filter behavior
|
||||||
|
- link/vote/result edge cases
|
||||||
|
- OpenAPI operation id stability
|
||||||
|
- CI-local parity script (`scripts/ci-local.ps1`) mirrors pipeline flow.
|
||||||
|
|
||||||
|
### 2.10 Tooling and contract discipline
|
||||||
|
|
||||||
|
- OpenAPI generated during build (`openapi/GameList.json`).
|
||||||
|
- JS client generated from OpenAPI with required operation-id checks.
|
||||||
|
- Separate lint + format + tests + coverage threshold checks.
|
||||||
|
- Build configured with warnings as errors in CI/local script.
|
||||||
|
|
||||||
|
## 3) Concrete feature set currently implemented
|
||||||
|
|
||||||
|
Use this as a reusable "starter scope menu" for the new app:
|
||||||
|
|
||||||
|
- Auth:
|
||||||
|
- register/login/logout
|
||||||
|
- owner bootstrap via admin key
|
||||||
|
- immutable display name post-registration
|
||||||
|
- auth options endpoint for registration UX
|
||||||
|
- Identity/session:
|
||||||
|
- cookie claim identity with admin claim
|
||||||
|
- stale/deleted-account cookie invalidation
|
||||||
|
- absolute session lifetime enforcement
|
||||||
|
- State:
|
||||||
|
- `/api/state`, `/api/me`
|
||||||
|
- phase next/prev
|
||||||
|
- SSE state invalidation
|
||||||
|
- etag conditional state reads
|
||||||
|
- Suggestions:
|
||||||
|
- create/update/delete/mine/all
|
||||||
|
- phase gating + admin override behavior
|
||||||
|
- suggestion cap + joker path
|
||||||
|
- link metadata exposure
|
||||||
|
- screenshot URL and reachability validation
|
||||||
|
- Votes:
|
||||||
|
- vote upsert
|
||||||
|
- finalize/unfinalize
|
||||||
|
- linked suggestion vote fan-out
|
||||||
|
- conflict handling on concurrent insert/update
|
||||||
|
- Results:
|
||||||
|
- gated by phase + admin-open flag
|
||||||
|
- ordered leaderboard with aggregates and voter metadata
|
||||||
|
- per-user own vote context
|
||||||
|
- Admin:
|
||||||
|
- results open/close toggle with phase realignment
|
||||||
|
- vote status panel
|
||||||
|
- joker grant
|
||||||
|
- player phase correction
|
||||||
|
- admin role grant/revoke (owner protected)
|
||||||
|
- player delete with cascades and password confirmation
|
||||||
|
- link/unlink suggestions with vote reset and unfinalize behavior
|
||||||
|
- reset/factory reset with password confirmation
|
||||||
|
|
||||||
|
## 4) REVIEW.md unresolved topics -> new-project design defaults
|
||||||
|
|
||||||
|
This section translates outstanding review risks into early decisions for the new app.
|
||||||
|
|
||||||
|
### 4.1 Data store scalability
|
||||||
|
|
||||||
|
Review concern:
|
||||||
|
- SQLite bottlenecks under higher write concurrency and multi-node scaling.
|
||||||
|
|
||||||
|
New-project default:
|
||||||
|
- Start with PostgreSQL (or SQL Server) for production profile.
|
||||||
|
- Keep provider abstraction and provider-specific migration strategy from day one.
|
||||||
|
- Keep SQLite only as local dev/test convenience if needed.
|
||||||
|
|
||||||
|
### 4.2 Workflow extensibility
|
||||||
|
|
||||||
|
Review concern:
|
||||||
|
- Workflow transitions are hard-coded in many places.
|
||||||
|
|
||||||
|
New-project default:
|
||||||
|
- Define transitions in a single state-machine table/model.
|
||||||
|
- Drive backend authorization and frontend navigation from same transition metadata.
|
||||||
|
- Add tests that validate the transition table itself.
|
||||||
|
|
||||||
|
### 4.3 Authorization model growth
|
||||||
|
|
||||||
|
Review concern:
|
||||||
|
- Role booleans (`IsAdmin`, `IsOwner`) limit future permission expansion.
|
||||||
|
|
||||||
|
New-project default:
|
||||||
|
- Use role/permission tables or claims-based capabilities.
|
||||||
|
- Keep owner as a protected capability, not a special-case boolean spread across code.
|
||||||
|
- Use policy-based authorization with explicit capability names.
|
||||||
|
|
||||||
|
### 4.4 Frontend maintainability
|
||||||
|
|
||||||
|
Review concern:
|
||||||
|
- String-template-heavy UI + global mutable state can become fragile.
|
||||||
|
|
||||||
|
New-project default:
|
||||||
|
- Move to TypeScript (or strict JSDoc typing) early.
|
||||||
|
- Keep module boundaries by feature.
|
||||||
|
- Keep explicit escaping/safe-url guards and DOM `textContent` standards.
|
||||||
|
- If not using framework, introduce a small typed view-model layer.
|
||||||
|
|
||||||
|
### 4.5 In-memory cache bounds
|
||||||
|
|
||||||
|
Review concern:
|
||||||
|
- Unbounded dictionaries may grow under high-cardinality traffic.
|
||||||
|
|
||||||
|
New-project default:
|
||||||
|
- Replace unbounded maps with bounded `MemoryCache` (size + TTL + eviction).
|
||||||
|
- For distributed deployments, use Redis with cardinality and TTL controls.
|
||||||
|
|
||||||
|
### 4.6 Linking/results query scaling
|
||||||
|
|
||||||
|
Review concern:
|
||||||
|
- Link and result workflows currently pull full sets into memory.
|
||||||
|
|
||||||
|
New-project default:
|
||||||
|
- Persist link-group ids and compute aggregates in SQL.
|
||||||
|
- Add pagination/windowing for large result sets.
|
||||||
|
- Benchmark query plans on realistic volumes.
|
||||||
|
|
||||||
|
### 4.7 External URL validation latency
|
||||||
|
|
||||||
|
Review concern:
|
||||||
|
- Reachability validation happens synchronously on write path.
|
||||||
|
|
||||||
|
New-project default:
|
||||||
|
- Accept user URL quickly, validate asynchronously (job queue/background worker), store validation status.
|
||||||
|
- Optionally proxy or prefetch media through controlled media service.
|
||||||
|
|
||||||
|
## 5) Topics from REVIEW that are already fixed and should be carried forward
|
||||||
|
|
||||||
|
- Polling amplification has been reduced through SSE + etag conditional reads.
|
||||||
|
- CSRF protection is explicit (same-origin validation for authenticated mutating requests).
|
||||||
|
- Password hashing is versioned and modernized (Argon2id current, transparent upgrades).
|
||||||
|
- CSP is tightened compared to prior permissive baseline (no inline style allowance, no insecure image origins).
|
||||||
|
- API drift risk reduced with OpenAPI generation + generated frontend client.
|
||||||
|
|
||||||
|
These are not optional add-ons; they should be baseline in the new app.
|
||||||
|
|
||||||
|
## 6) New-project starter checklist
|
||||||
|
|
||||||
|
- Bootstrap:
|
||||||
|
- choose production DB provider first
|
||||||
|
- define transition/state-machine model before endpoint coding
|
||||||
|
- define permission/capability model before admin features
|
||||||
|
- Security:
|
||||||
|
- cookie or token strategy finalized with CSRF model
|
||||||
|
- rate limiting partitions and thresholds defined
|
||||||
|
- strict CSP and security headers in first commit
|
||||||
|
- versioned password hashing with migration strategy
|
||||||
|
- trusted proxy/host settings explicit
|
||||||
|
- Contract:
|
||||||
|
- OpenAPI generation enabled in build
|
||||||
|
- generated client wired into frontend
|
||||||
|
- operation-id stability tested
|
||||||
|
- Data integrity:
|
||||||
|
- enforce critical invariants both app-side and DB-side
|
||||||
|
- transaction boundaries for multi-entity admin actions
|
||||||
|
- Frontend:
|
||||||
|
- module boundaries and state refresh model defined
|
||||||
|
- escaping/url-safe helpers mandatory
|
||||||
|
- i18n structure and fallback behavior in place
|
||||||
|
- Testing:
|
||||||
|
- integration test host with real migrations
|
||||||
|
- deterministic stubs for network dependencies
|
||||||
|
- coverage gate enforced in local + CI scripts
|
||||||
|
|
||||||
|
## 7) Keep/avoid quick reference
|
||||||
|
|
||||||
|
Keep:
|
||||||
|
- Thin endpoints + workflow services.
|
||||||
|
- Shared service result abstraction.
|
||||||
|
- Explicit middleware order.
|
||||||
|
- SSE + ETag state sync.
|
||||||
|
- Generated API client from OpenAPI.
|
||||||
|
- DB-enforced invariants.
|
||||||
|
- Regression tests for security-sensitive UI rendering.
|
||||||
|
|
||||||
|
Avoid:
|
||||||
|
- Hard-coded workflow transitions scattered in backend/frontend.
|
||||||
|
- Boolean-only role model for long-term products.
|
||||||
|
- Unbounded in-memory caches.
|
||||||
|
- Synchronous external network checks on hot write paths.
|
||||||
|
- Manual API contract duplication between docs/frontend/backend.
|
||||||
|
|
||||||
Reference in New Issue
Block a user