# Website Architecture Contract (Strict) This document is the **authoritative contract** for `apps/website`. If any other website document conflicts with this one, **this one wins**. ## 1) Purpose (non-negotiable) The website is a **delivery layer**. It does **not**: - contain business rules - make authorization decisions - own or persist business truth It **only**: - renders truth from `apps/api` - collects user intent - forwards user intent to `apps/api` The API is the single source of truth. ## 2) System context (hard boundary) The website never bypasses the API. ```text Browser ↓ Next.js App Router (RSC + Server Actions) ↓ HTTP ↓ Backend API (Use Cases, Domain, Database) ``` ## 3) Website presentation model types (strict) ### 3.1 API Transport DTO Definition: the shape returned by the backend API over HTTP. Rules: - API Transport DTOs MUST be contained inside infrastructure. - API Transport DTOs MUST NOT be imported by Templates. Canonical placement in this repo: - `apps/website/lib/types/**` (transport DTOs consumed by services and page queries) ### 3.2 Page DTO Definition: the website-owned, server-to-client payload for a route. Rules: - JSON-serializable only. - Contains **raw** values only (IDs, ISO strings, numbers, codes). - MUST NOT contain class instances. - Created by Page Queries. - Passed from server routes into client code. Canonical placement in this repo: - `apps/website/lib/page-queries/**` (composition and Page DTO construction) ### 3.3 ViewModel Definition: the client-only, UI-owned class representing fully prepared UI state. Rules: - Instantiated only in `'use client'` modules. - Never serialized. - MUST NOT be passed into Templates. See [`VIEW_MODELS.md`](docs/architecture/website/VIEW_MODELS.md:1). Canonical placement in this repo: - `apps/website/lib/view-models/**` ### 3.4 ViewData Definition: the only allowed input type for Templates. Rules: - JSON-serializable only. - Contains only template-ready values (mostly strings/numbers/booleans). - MUST NOT contain class instances. See [`VIEW_DATA.md`](docs/architecture/website/VIEW_DATA.md:1). Canonical placement in this repo: - `apps/website/templates/**` (Templates that accept ViewData only) ## 4) Presentation helpers (strict) ### 4.1 Presenter Definition: a deterministic, side-effect free transformation. Presenters map between website presentation models: - Page DTO → ViewData - Page DTO → ViewModel - ViewModel → ViewData Rules: - MUST be deterministic. - MUST be side-effect free. - MUST NOT call HTTP. - MUST NOT call the API. - MAY use Display Objects. See [`PRESENTERS.md`](docs/architecture/website/PRESENTERS.md:1). Canonical placement in this repo: - colocated with ViewModels in `apps/website/lib/view-models/**` ### 4.2 Display Object Definition: deterministic, reusable, UI-only formatting/mapping logic. Rules: - Class-based, immutable, deterministic. - MUST NOT call `Intl.*`. - MUST NOT call any `toLocale*`. - MUST NOT implement business rules. See [`DISPLAY_OBJECTS.md`](docs/architecture/website/DISPLAY_OBJECTS.md:1). Canonical placement in this repo: - `apps/website/lib/display-objects/**` ## 5) Read flow (strict) ```text RSC page.tsx ↓ PageQuery (server) ↓ API service / API client (infra) ↓ API Transport DTO ↓ Page DTO ↓ Presenter (client) ↓ ViewModel (optional, client) ↓ Presenter (client) ↓ ViewData ↓ Template ``` ## 6) Write flow (strict) All writes MUST enter through **Next.js Server Actions**. Forbidden: - client components performing write HTTP requests - client components calling API clients for mutations Allowed: - client submits intent (FormData, button action) - server action performs UX validation - server action calls the API See [`FORM_SUBMISSION.md`](docs/architecture/website/FORM_SUBMISSION.md:1). ## 7) Authorization (strict) - The website may hide/disable UI for UX. - The website MUST NOT enforce security. - The API enforces authentication and authorization. See [`docs/architecture/shared/BLOCKERS_AND_GUARDS.md`](docs/architecture/shared/BLOCKERS_AND_GUARDS.md:1) and [`docs/architecture/website/BLOCKERS.md`](docs/architecture/website/BLOCKERS.md:1). ## 7.1) Client state (strict) Client-side state is allowed only for UI concerns. Allowed: - selection - open/closed dialogs - transient form state - optimistic flags and loading spinners Forbidden: - treating client state as business truth - using client state as an authorization decision - persisting client state as the source of truth Hard rule: - any truth returned by the API MUST overwrite client assumptions. Canonical placement in this repo: - `apps/website/lib/blockers/**` for UX-only prevention helpers - `apps/website/lib/hooks/**` for React-only utilities - `apps/website/lib/command-models/**` for transient form models See [`CLIENT_STATE.md`](docs/architecture/website/CLIENT_STATE.md:1). ## 8) DI contract (Inversify) (strict) The DI system under [`apps/website/lib/di/index.ts`](apps/website/lib/di/index.ts:1) is **client-first**. Server execution is concurrent. Any shared singleton container can leak cross-request state. Rules: 1. Server `app/**/page.tsx` MUST NOT access the container. 2. Page Queries SHOULD prefer manual wiring. 3. Client modules MAY use DI via [`ContainerProvider`](apps/website/lib/di/index.ts:11) and hooks. 4. [`ContainerManager.getContainer()`](apps/website/lib/di/container.ts:74) is **client-only**. 5. Any server DI usage MUST be request-scoped (a fresh container per request). Hard constraint: - A singleton Inversify container MUST NOT be used to serve concurrent server requests. See [`WEBSITE_DI_RULES.md`](docs/architecture/website/WEBSITE_DI_RULES.md:1). ## 9) Non-negotiable rules (final) 1. The API is the brain. 2. The website is a terminal. 3. API Transport DTOs never reach Templates. 4. ViewModels never go to the API. 5. Templates accept ViewData only. 6. Page Queries do not format; they only compose. 7. Presenters are pure and deterministic. 8. Server Actions are the only write entry point. 9. Authorization always belongs to the API.