website refactor
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user