website refactor

This commit is contained in:
2026-01-12 19:24:59 +01:00
parent 1f0c4f7fa6
commit 5ea95eaf51
54 changed files with 2894 additions and 2342 deletions

View File

@@ -51,39 +51,20 @@ Canonical placement in this repo:
- `apps/website/lib/types/**` (transport DTOs consumed by services and page queries)
### 3.2 Page DTO
### 3.2 API Transport DTO
Definition: the website-owned, server-to-client payload for a route.
Definition: the shape returned by the backend API over HTTP.
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.
- 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/page-queries/**` (composition and Page DTO construction)
- `apps/website/lib/types/**` (transport DTOs consumed by services and page queries)
### 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
### 3.3 ViewData
Definition: the only allowed input type for Templates.
@@ -99,17 +80,29 @@ Canonical placement in this repo:
- `apps/website/templates/**` (Templates that accept ViewData only)
## 4) Presentation helpers (strict)
### 3.3 ViewModel
### 4.1 Presenter
Definition: the client-only, UI-owned class representing fully prepared UI state.
Definition: a deterministic, side-effect free transformation.
Rules:
Presenters map between website presentation models:
- Instantiated only in `'use client'` modules.
- Never serialized.
- Used for client components that need state management.
- Page DTO → ViewData
- Page DTO → ViewModel
- ViewModel → ViewData
See [`VIEW_MODELS.md`](docs/architecture/website/VIEW_MODELS.md:1).
Canonical placement in this repo:
- `apps/website/lib/view-models/**`
## 4) Data transformation helpers (strict)
### 4.1 ViewModel Builder
Definition: transforms API Transport DTOs into ViewModels.
Purpose: prepare raw API data for client-side state management.
Rules:
@@ -117,15 +110,37 @@ Rules:
- MUST be side-effect free.
- MUST NOT call HTTP.
- MUST NOT call the API.
- MAY use Display Objects.
- Input: API Transport DTO
- Output: ViewModel
See [`PRESENTERS.md`](docs/architecture/website/PRESENTERS.md:1).
See [`BUILDERS.md`](docs/architecture/website/BUILDERS.md:1).
Canonical placement in this repo:
- `apps/website/lib/presenters/**`
- `apps/website/lib/builders/view-models/**`
### 4.2 Display Object
### 4.2 ViewData Builder
Definition: transforms API DTOs directly into ViewData for templates.
Purpose: prepare API data for server-side rendering.
Rules:
- MUST be deterministic.
- MUST be side-effect free.
- MUST NOT call HTTP.
- MUST NOT call the API.
- Input: API Transport DTO
- Output: ViewData
See [`BUILDERS.md`](docs/architecture/website/BUILDERS.md:1).
Canonical placement in this repo:
- `apps/website/lib/builders/view-data/**`
### 4.3 Display Object
Definition: deterministic, reusable, UI-only formatting/mapping logic.
@@ -144,28 +159,40 @@ Canonical placement in this repo:
## 5) Read flow (strict)
### Server Components (RSC)
```text
RSC page.tsx
PageQuery (server)
PageQuery
API service / API client (infra)
API client (infra)
API Transport DTO
Page DTO
Presenter (client)
ViewModel (optional, client)
Presenter (client)
ViewData Builder (lib/builders/view-data/)
ViewData
Template
```
### Client Components
```text
Client Component
API client (useEffect)
API Transport DTO
ViewModel Builder (lib/builders/view-models/)
ViewModel (lib/view-models/)
Client State (useState)
Template
```
## 6) Write flow (strict)
All writes MUST enter through **Next.js Server Actions**.
@@ -179,9 +206,68 @@ Allowed:
- client submits intent (FormData, button action)
- server action performs UX validation
- server action calls the API
- **server action calls a service** (not API clients directly)
- service orchestrates API calls and business logic
See [`FORM_SUBMISSION.md`](docs/architecture/website/FORM_SUBMISSION.md:1).
**Server Actions must use Services:**
```typescript
// ❌ WRONG - Direct API client usage
'use server';
import { AdminApiClient } from '@/lib/api/admin/AdminApiClient';
export async function updateUserStatus(userId: string, status: string) {
const apiClient = new AdminApiClient(...);
await apiClient.updateUserStatus(userId, status); // ❌ Should use service
}
// ✅ CORRECT - Service usage
'use server';
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
import { AdminApiClient } from '@/lib/api/admin/AdminApiClient';
import { AdminService } from '@/lib/services/admin/AdminService';
import { revalidatePath } from 'next/cache';
export async function updateUserStatus(userId: string, status: string) {
try {
// Create infrastructure
const logger = new ConsoleLogger();
const errorReporter = new EnhancedErrorReporter(logger, {
showUserNotifications: true,
logToConsole: true,
reportToExternal: process.env.NODE_ENV === 'production',
});
const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001';
const apiClient = new AdminApiClient(baseUrl, errorReporter, logger);
const service = new AdminService(apiClient);
// Use service
await service.updateUserStatus(userId, status);
// Revalidate
revalidatePath('/admin/users');
} catch (error) {
console.error('updateUserStatus failed:', error);
throw new Error('Failed to update user status');
}
}
```
**Pattern**:
1. Server action creates infrastructure (logger, errorReporter, apiClient)
2. Server action creates service with infrastructure
3. Server action calls service method
4. Server action handles revalidation and returns
**Rationale**:
- Services orchestrate API calls (can grow to multiple calls)
- Keeps server actions consistent with PageQueries
- Makes infrastructure explicit and testable
- Services can add caching, retries, transformations
See [`FORM_SUBMISSION.md`](docs/architecture/website/FORM_SUBMISSION.md:1) and [`SERVICES.md`](docs/architecture/website/SERVICES.md:1).
## 7) Authorization (strict)
@@ -245,9 +331,12 @@ See [`WEBSITE_DI_RULES.md`](docs/architecture/website/WEBSITE_DI_RULES.md:1).
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.
4. Templates accept ViewData only.
5. Page Queries do not format; they only compose.
6. ViewData Builders transform API DTO → ViewData (RSC).
7. ViewModel Builders transform API DTO → ViewModel (Client).
8. Builders are pure and deterministic.
9. Server Actions are the only write entry point.
10. Server Actions must use Mutations (not Services directly).
11. Mutations orchestrate Services for writes.
12. Authorization always belongs to the API.