106 lines
2.8 KiB
Markdown
106 lines
2.8 KiB
Markdown
# 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 <AdminDashboardTemplate viewData={viewData} />;
|
|
}
|
|
```
|
|
|
|
### 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<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`](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)
|