Files
gridpilot.gg/plans/website-architecture-violations.md
2026-01-11 15:06:26 +01:00

12 KiB
Raw Blame History

Website Architecture Violations Report

Scope: apps/website/** aligned against docs/architecture/website/**, with the authoritative contract at WEBSITE_CONTRACT.md.

This report lists violations as: rule ⇒ evidence ⇒ impact ⇒ fix direction.


A) DI contract violations (server-side singleton container usage)

Rule Evidence Why this is a violation Fix direction
Server app/**/page.tsx MUST NOT access the DI container (WEBSITE_CONTRACT.md, WEBSITE_DI_RULES.md) ContainerManager.getInstance().getContainer() Server route is directly resolving from a singleton container; violates the client-first DI contract and request-safety. Replace with a PageQuery + manual per-request wiring, or a request-scoped container factory.
ContainerManager.getContainer() is client-only (WEBSITE_DI_RULES.md) PageDataFetcher.fetch() calls ContainerManager.getInstance().getContainer() This helper enables forbidden server access to the singleton container. Ban server use of PageDataFetcher.fetch(). Use explicit construction in PageQueries.
PageQueries may use DI only if request-scoped and stateless (WEBSITE_DI_RULES.md) DashboardPageQuery.execute() uses ContainerManager.getInstance().getContainer() PageQuery runs on the server; the singleton container is explicitly unsafe for concurrent server requests. Convert to manual wiring (construct API client + service per call), or create a fresh container per request.

B) RSC route structure violations (page.tsx doing more than composition)

Rule Evidence Why this is a violation Fix direction
page.tsx does composition only (WEBSITE_FILE_STRUCTURE.md, WEBSITE_RSC_PRESENTATION.md) Data fetch + categorization + inline template in a server route module: fetchProfileLeaguesData() and ProfileLeaguesTemplate() Responsibilities are mixed: server data fetching, decision logic, and rendering composition in one module. Split into per-route shape: page.tsx calls a PageQuery and passes Page DTO; a *PageClient.tsx builds ViewData; Template renders ViewData only.
PageQueries MUST return the documented discriminated union (WEBSITE_PAGE_QUERIES.md) Local type differs from spec: PageQueryResult<TData> uses data and destination Breaks the documented contract (ok with dto, redirect with to). Standardize a single PageQueryResult type per WEBSITE_PAGE_QUERIES.md and enforce across queries.

C) ViewModel boundary violations (ViewModels used/created on server and/or passed into Templates)

Rule Evidence Why this is a violation Fix direction
ViewModels are client-only and never serialized (WEBSITE_CONTRACT.md, VIEW_MODELS.md) Server route data types include ViewModels: TeamDetailData.team: TeamDetailsViewModel and are passed into Template wrapper: TeamDetailTemplateWrapper() Implies ViewModels cross the server→client boundary, contradicting the “client-only class” rule and risking serialization/hydration issues. Use Page DTOs for server-to-client, and instantiate ViewModels only in 'use client' modules.
Templates accept ViewData only (WEBSITE_CONTRACT.md, VIEW_DATA.md) Template prop uses ViewModel instances: DriverRankingsTemplateProps.drivers: DriverLeaderboardItemViewModel[] Template is no longer receiving ViewData (JSON-serializable primitive shapes). Introduce Presenters that map Page DTO → ViewData and pass ViewData into Templates.
PageQueries must not instantiate client-only types / ViewModels (WEBSITE_PAGE_QUERIES.md) PageQuery imports from lib/view-models/**: DashboardOverviewViewModelData lib/view-models/** is the client-only boundary (also where Presenters live per PRESENTERS.md). Move Page DTO types under lib/page-queries/** and keep view-model types under lib/view-models/**.

D) Template purity violations (Templates importing forbidden layers and doing computation)

Rule Evidence Why this is a violation Fix direction
Templates MUST NOT import ViewModels (WEBSITE_GUARDRAILS.md) Example: DriverLeaderboardItemViewModel Template depends on client-only classes, drifting into Presenter/ViewModel responsibilities. Replace ViewModel props with ViewData props.
Templates MUST NOT import Display Objects (WEBSITE_GUARDRAILS.md) Example: LeagueRoleDisplay Display Objects are forbidden inside Templates. Use Presenters/ViewModels to emit primitive strings into ViewData.
Templates MUST NOT compute derived values (VIEW_DATA.md) Filtering/sorting occurs inside Template: drivers.filter() and sort(...) Template performs orchestration and non-trivial computation rather than rendering prepared ViewData. Move to ViewModels (client) and/or Presenters; Templates render only.
“Templates are pure” (WEBSITE_FILE_STRUCTURE.md) Hooks in templates: useMemo() used for filtering/grouping Pure-template contract is violated by stateful/reactive computations. Create a *PageClient.tsx container for state/computation; keep template as pure function over ViewData.

E) Determinism violations: forbidden locale/time formatting paths (Intl.*, toLocale*)

Rule Evidence Why this is a violation Fix direction
Templates must not call locale APIs (VIEW_DATA.md, WEBSITE_GUARDRAILS.md) Example: toLocaleDateString() Locale/timezone-dependent formatting can differ between SSR and browser, risking hydration mismatches and non-determinism. Replace with deterministic formatting via Display Objects, or API-provided labels, passed through Presenters into ViewData.
Formatting code paths must not use locale APIs (DISPLAY_OBJECTS.md, VIEW_DATA.md) Example: toLocaleString() Even though ViewModels may format, this repos strict determinism rules forbid runtime-locale APIs. Replace with deterministic numeric/date formatting helpers (no Intl.*, no toLocale*).
Utility helpers used in presentation must not use locale APIs (WEBSITE_GUARDRAILS.md) formatDate() calls toLocaleDateString() Utilities become hidden sources of non-determinism across the app. Replace these helpers with deterministic formatters and enforce via guardrail tests/ESLint.

F) Page.tsx guardrail violations (sorting/filtering in server routes and forbidden imports)

Rule Evidence Why this is a violation Fix direction
RSC page.tsx must not perform sorting/filtering beyond trivial checks (WEBSITE_GUARDRAILS.md) Server route computes derived values via reduce/filter/sort: computeDerivedData() Server route contains presentation shaping. Move shaping into PageQuery output (as Page DTO only) and/or into client ViewModel/Presenter.
RSC page.tsx must not import ViewModels (WEBSITE_GUARDRAILS.md) Example: TeamSummaryViewModel ViewModels are client-only. Replace with Page DTO types; construct ViewModels client-side.

G) Write-flow violations (writes not entering through Server Actions)

Rule Evidence Why this is a violation Fix direction
All writes MUST enter through Server Actions (WEBSITE_CONTRACT.md, FORM_SUBMISSION.md) Client component performs POST: fetch('/api/auth/logout', { method: 'POST' }) Client-initiated writes are explicitly forbidden. Replace with a Server Action invoked via <form action={...}> or button action, then revalidate/navigate.

H) UX-only blockers embedded in services (state leakage risk)

Rule Evidence Why this is a violation Fix direction
Blockers are UX-only and should be local + reversible (BLOCKERS.md, CLIENT_STATE.md) Stateful blockers inside a service: submitBlocker and throttle If the service is DI-singleton or reused across requests, this state can leak across users/requests; also mixes UI concerns into a service boundary. Move blockers into client UI boundary (hook/component) or ensure strict client-only, per-instance usage. Services should remain stateless.

High-signal file sets (pattern-based indicators)

Templates importing ViewModels and or Display Objects (forbidden)

Server routes importing ViewModels and or doing formatting/filtering (forbidden/discouraged)

Structural drift vs canonical lib/* layout