2.8 KiB
2.8 KiB
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.
1) Core rule: API owns business truth
apps/apiis the only source of truth for business rules and canonical filtering/sorting.apps/websiteis 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.
3) Required per-route structure
Server Components (RSC)
Every RSC route MUST follow:
page.tsx: calls a PageQuerypage.tsx: builds ViewData using ViewDataBuilderpage.tsx: renders Template with ViewData
Example:
export default async function AdminDashboardPage() {
const apiDto = await AdminDashboardPageQuery.execute();
const viewData = AdminDashboardViewDataBuilder.build(apiDto);
return <AdminDashboardTemplate viewData={viewData} />;
}
Client Components
Client components that need API data MUST follow:
*Client.tsx: fetches API DTO*Client.tsx: builds ViewModel using ViewModelBuilder*Client.tsx: renders Template with ViewModel
Example:
'use client';
export function AdminDashboardClient() {
const [viewModel, setViewModel] = useState<AdminDashboardViewModel | null>(null);
useEffect(() => {
const apiDto = await adminApiClient.getDashboard();
const vm = AdminDashboardViewModelBuilder.build(apiDto);
setViewModel(vm);
}, []);
return viewModel ? <AdminDashboardTemplate viewModel={viewModel} /> : null;
}
All writes enter through Server Actions.
See FORM_SUBMISSION.md.
4) Authoritative specification
This document is an entry point only.
The authoritative, test-enforced spec lives at:
Final contract: