docs
This commit is contained in:
253
docs/architecture/website/WEBSITE_CONTRACT.md
Normal file
253
docs/architecture/website/WEBSITE_CONTRACT.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# Website Architecture Contract (Strict)
|
||||
|
||||
This document is the **authoritative contract** for `apps/website`.
|
||||
|
||||
If any other website document conflicts with this one, **this one wins**.
|
||||
|
||||
## 1) Purpose (non-negotiable)
|
||||
|
||||
The website is a **delivery layer**.
|
||||
|
||||
It does **not**:
|
||||
|
||||
- contain business rules
|
||||
- make authorization decisions
|
||||
- own or persist business truth
|
||||
|
||||
It **only**:
|
||||
|
||||
- renders truth from `apps/api`
|
||||
- collects user intent
|
||||
- forwards user intent to `apps/api`
|
||||
|
||||
The API is the single source of truth.
|
||||
|
||||
## 2) System context (hard boundary)
|
||||
|
||||
The website never bypasses the API.
|
||||
|
||||
```text
|
||||
Browser
|
||||
↓
|
||||
Next.js App Router (RSC + Server Actions)
|
||||
↓
|
||||
HTTP
|
||||
↓
|
||||
Backend API (Use Cases, Domain, Database)
|
||||
```
|
||||
|
||||
## 3) Website presentation model types (strict)
|
||||
|
||||
### 3.1 API Transport DTO
|
||||
|
||||
Definition: the shape returned by the backend API over HTTP.
|
||||
|
||||
Rules:
|
||||
|
||||
- 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/types/**` (transport DTOs consumed by services and page queries)
|
||||
|
||||
### 3.2 Page DTO
|
||||
|
||||
Definition: the website-owned, server-to-client payload for a route.
|
||||
|
||||
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.
|
||||
|
||||
Canonical placement in this repo:
|
||||
|
||||
- `apps/website/lib/page-queries/**` (composition and Page DTO construction)
|
||||
|
||||
### 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
|
||||
|
||||
Definition: the only allowed input type for Templates.
|
||||
|
||||
Rules:
|
||||
|
||||
- JSON-serializable only.
|
||||
- Contains only template-ready values (mostly strings/numbers/booleans).
|
||||
- MUST NOT contain class instances.
|
||||
|
||||
See [`VIEW_DATA.md`](docs/architecture/website/VIEW_DATA.md:1).
|
||||
|
||||
Canonical placement in this repo:
|
||||
|
||||
- `apps/website/templates/**` (Templates that accept ViewData only)
|
||||
|
||||
## 4) Presentation helpers (strict)
|
||||
|
||||
### 4.1 Presenter
|
||||
|
||||
Definition: a deterministic, side-effect free transformation.
|
||||
|
||||
Presenters map between website presentation models:
|
||||
|
||||
- Page DTO → ViewData
|
||||
- Page DTO → ViewModel
|
||||
- ViewModel → ViewData
|
||||
|
||||
Rules:
|
||||
|
||||
- MUST be deterministic.
|
||||
- MUST be side-effect free.
|
||||
- MUST NOT call HTTP.
|
||||
- MUST NOT call the API.
|
||||
- MAY use Display Objects.
|
||||
|
||||
See [`PRESENTERS.md`](docs/architecture/website/PRESENTERS.md:1).
|
||||
|
||||
Canonical placement in this repo:
|
||||
|
||||
- colocated with ViewModels in `apps/website/lib/view-models/**`
|
||||
|
||||
### 4.2 Display Object
|
||||
|
||||
Definition: deterministic, reusable, UI-only formatting/mapping logic.
|
||||
|
||||
Rules:
|
||||
|
||||
- Class-based, immutable, deterministic.
|
||||
- MUST NOT call `Intl.*`.
|
||||
- MUST NOT call any `toLocale*`.
|
||||
- MUST NOT implement business rules.
|
||||
|
||||
See [`DISPLAY_OBJECTS.md`](docs/architecture/website/DISPLAY_OBJECTS.md:1).
|
||||
|
||||
Canonical placement in this repo:
|
||||
|
||||
- `apps/website/lib/display-objects/**`
|
||||
|
||||
## 5) Read flow (strict)
|
||||
|
||||
```text
|
||||
RSC page.tsx
|
||||
↓
|
||||
PageQuery (server)
|
||||
↓
|
||||
API service / API client (infra)
|
||||
↓
|
||||
API Transport DTO
|
||||
↓
|
||||
Page DTO
|
||||
↓
|
||||
Presenter (client)
|
||||
↓
|
||||
ViewModel (optional, client)
|
||||
↓
|
||||
Presenter (client)
|
||||
↓
|
||||
ViewData
|
||||
↓
|
||||
Template
|
||||
```
|
||||
|
||||
## 6) Write flow (strict)
|
||||
|
||||
All writes MUST enter through **Next.js Server Actions**.
|
||||
|
||||
Forbidden:
|
||||
|
||||
- client components performing write HTTP requests
|
||||
- client components calling API clients for mutations
|
||||
|
||||
Allowed:
|
||||
|
||||
- client submits intent (FormData, button action)
|
||||
- server action performs UX validation
|
||||
- server action calls the API
|
||||
|
||||
See [`FORM_SUBMISSION.md`](docs/architecture/website/FORM_SUBMISSION.md:1).
|
||||
|
||||
## 7) Authorization (strict)
|
||||
|
||||
- The website may hide/disable UI for UX.
|
||||
- The website MUST NOT enforce security.
|
||||
- The API enforces authentication and authorization.
|
||||
|
||||
See [`BLOCKER_GUARDS.md`](docs/architecture/website/BLOCKER_GUARDS.md:1).
|
||||
|
||||
## 7.1) Client state (strict)
|
||||
|
||||
Client-side state is allowed only for UI concerns.
|
||||
|
||||
Allowed:
|
||||
|
||||
- selection
|
||||
- open/closed dialogs
|
||||
- transient form state
|
||||
- optimistic flags and loading spinners
|
||||
|
||||
Forbidden:
|
||||
|
||||
- treating client state as business truth
|
||||
- using client state as an authorization decision
|
||||
- persisting client state as the source of truth
|
||||
|
||||
Hard rule:
|
||||
|
||||
- any truth returned by the API MUST overwrite client assumptions.
|
||||
|
||||
Canonical placement in this repo:
|
||||
|
||||
- `apps/website/lib/blockers/**` for UX-only prevention helpers
|
||||
- `apps/website/lib/hooks/**` for React-only utilities
|
||||
- `apps/website/lib/command-models/**` for transient form models
|
||||
|
||||
See [`CLIENT_STATE.md`](docs/architecture/website/CLIENT_STATE.md:1).
|
||||
|
||||
## 8) DI contract (Inversify) (strict)
|
||||
|
||||
The DI system under [`apps/website/lib/di/index.ts`](apps/website/lib/di/index.ts:1) is **client-first**.
|
||||
|
||||
Server execution is concurrent. Any shared singleton container can leak cross-request state.
|
||||
|
||||
Rules:
|
||||
|
||||
1. Server `app/**/page.tsx` MUST NOT access the container.
|
||||
2. Page Queries SHOULD prefer manual wiring.
|
||||
3. Client modules MAY use DI via [`ContainerProvider`](apps/website/lib/di/index.ts:11) and hooks.
|
||||
4. [`ContainerManager.getContainer()`](apps/website/lib/di/container.ts:74) is **client-only**.
|
||||
5. Any server DI usage MUST be request-scoped (a fresh container per request).
|
||||
|
||||
Hard constraint:
|
||||
|
||||
- A singleton Inversify container MUST NOT be used to serve concurrent server requests.
|
||||
|
||||
See [`WEBSITE_DI_RULES.md`](docs/architecture/website/WEBSITE_DI_RULES.md:1).
|
||||
|
||||
## 9) Non-negotiable rules (final)
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user