# Website RSC Presentation Architecture (Strict) This document defines the only allowed presentation architecture for `apps/website` (Next.js App Router). It is **website-only** and does not change `apps/api` or `core` architecture. Authoritative contract: [`WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1). ## 1) Core rule: API owns business truth - `apps/api` is the only source of truth for business rules and canonical filtering/sorting. - `apps/website` is presentation infrastructure: composition, routing, caching, and rendering. ## 2) The three website presentation data types ### 2.1 Page DTO **Purpose:** server-to-client payload. **Rules:** - JSON-serializable only. - Contains raw values only (ISO date strings, numbers, codes). - MUST NOT contain class instances. ### 2.2 ViewModel **Purpose:** client-only presentation model. **Rules:** - Class-based. - Instantiated only in `'use client'` modules. - Composes Display Objects. - NEVER passed into Templates. ### 2.3 ViewData **Purpose:** Template input. **Rules:** - JSON-serializable only. - Contains only values ready to render (mostly strings/numbers). - Built from API DTO directly in RSC. The mapping between API DTO and ViewData is performed by ViewData Builders. See [`BUILDERS.md`](docs/architecture/website/BUILDERS.md:1). ## 3) Required per-route structure ### Server Components (RSC) Every RSC route MUST follow: 1) `page.tsx`: calls a PageQuery 2) `page.tsx`: builds ViewData using ViewDataBuilder 3) `page.tsx`: renders Template with ViewData Example: ```typescript export default async function AdminDashboardPage() { const apiDto = await AdminDashboardPageQuery.execute(); const viewData = AdminDashboardViewDataBuilder.build(apiDto); return ; } ``` ### Client Components Client components that need API data MUST follow: 1) `*Client.tsx`: fetches API DTO 2) `*Client.tsx`: builds ViewModel using ViewModelBuilder 3) `*Client.tsx`: renders Template with ViewModel Example: ```typescript 'use client'; export function AdminDashboardClient() { const [viewModel, setViewModel] = useState(null); useEffect(() => { const apiDto = await adminApiClient.getDashboard(); const vm = AdminDashboardViewModelBuilder.build(apiDto); setViewModel(vm); }, []); return viewModel ? : null; } ``` All writes enter through Server Actions. See [`FORM_SUBMISSION.md`](docs/architecture/website/FORM_SUBMISSION.md:1). ## 4) Authoritative specification This document is an entry point only. The authoritative, test-enforced spec lives at: - [plans/nextjs-rsc-viewmodels-concept.md](plans/nextjs-rsc-viewmodels-concept.md:1) Final contract: - [`WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1)