docs
This commit is contained in:
51
docs/architecture/website/BLOCKERS.md
Normal file
51
docs/architecture/website/BLOCKERS.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Blockers (Website UX)
|
||||
|
||||
This document defines **Blockers** as UX-only prevention mechanisms in the website.
|
||||
|
||||
Shared contract: [`docs/architecture/shared/BLOCKERS_AND_GUARDS.md`](docs/architecture/shared/BLOCKERS_AND_GUARDS.md:1)
|
||||
|
||||
## 1) Definition
|
||||
|
||||
A Blocker is a website mechanism that prevents an action from being executed.
|
||||
|
||||
Blockers exist solely to improve UX and reduce unnecessary requests.
|
||||
|
||||
Blockers are not security.
|
||||
|
||||
## 2) Responsibilities
|
||||
|
||||
Blockers MAY:
|
||||
|
||||
- prevent multiple submissions
|
||||
- disable actions temporarily
|
||||
- debounce or throttle interactions
|
||||
- hide or disable UI elements
|
||||
- prevent navigation under certain conditions
|
||||
|
||||
Blockers MUST:
|
||||
|
||||
- be reversible
|
||||
- be local to the website
|
||||
- be treated as best-effort helpers
|
||||
|
||||
## 3) Restrictions
|
||||
|
||||
Blockers MUST NOT:
|
||||
|
||||
- enforce security
|
||||
- claim authorization
|
||||
- block access permanently
|
||||
- replace API Guards
|
||||
- make assumptions about backend state
|
||||
|
||||
## 4) Common Blockers
|
||||
|
||||
- SubmitBlocker
|
||||
- ThrottleBlocker
|
||||
- NavigationBlocker
|
||||
- FeatureBlocker
|
||||
|
||||
## 5) Canonical placement
|
||||
|
||||
- `apps/website/lib/blockers/**`
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
Blockers & Guards
|
||||
|
||||
This document defines clear, non-overlapping responsibilities for Blockers (frontend) and Guards (backend).
|
||||
The goal is to prevent semantic drift, security confusion, and inconsistent implementations.
|
||||
|
||||
⸻
|
||||
|
||||
Core Principle
|
||||
|
||||
Guards enforce. Blockers prevent.
|
||||
• Guards protect the system.
|
||||
• Blockers protect the UX.
|
||||
|
||||
There are no exceptions to this rule.
|
||||
|
||||
⸻
|
||||
|
||||
Backend — Guards (NestJS)
|
||||
|
||||
Definition
|
||||
|
||||
A Guard is a backend mechanism that enforces access or execution rules.
|
||||
If a Guard denies execution, the request does not reach the application logic.
|
||||
|
||||
In NestJS, Guards implement CanActivate.
|
||||
|
||||
⸻
|
||||
|
||||
Responsibilities
|
||||
|
||||
Guards MAY:
|
||||
• block requests entirely
|
||||
• return HTTP errors (401, 403, 429)
|
||||
• enforce authentication and authorization
|
||||
• enforce rate limits
|
||||
• enforce feature availability
|
||||
• protect against abuse and attacks
|
||||
|
||||
Guards MUST:
|
||||
• be deterministic
|
||||
• be authoritative
|
||||
• be security-relevant
|
||||
|
||||
⸻
|
||||
|
||||
Restrictions
|
||||
|
||||
Guards MUST NOT:
|
||||
• depend on frontend state
|
||||
• contain UI logic
|
||||
• attempt to improve UX
|
||||
• assume the client behaved correctly
|
||||
|
||||
⸻
|
||||
|
||||
Common Backend Guards
|
||||
• AuthGuard
|
||||
• RolesGuard
|
||||
• PermissionsGuard
|
||||
• ThrottlerGuard (NestJS)
|
||||
• RateLimitGuard
|
||||
• CsrfGuard
|
||||
• FeatureFlagGuard
|
||||
|
||||
⸻
|
||||
|
||||
Summary (Backend)
|
||||
• Guards decide
|
||||
• Guards enforce
|
||||
• Guards secure the system
|
||||
|
||||
⸻
|
||||
|
||||
Frontend — Blockers
|
||||
|
||||
Definition
|
||||
|
||||
A Blocker is a frontend mechanism that prevents an action from being executed.
|
||||
Blockers exist solely to improve UX and reduce unnecessary requests.
|
||||
|
||||
Blockers are not security mechanisms.
|
||||
|
||||
⸻
|
||||
|
||||
Responsibilities
|
||||
|
||||
Blockers MAY:
|
||||
• prevent multiple submissions
|
||||
• disable actions temporarily
|
||||
• debounce or throttle interactions
|
||||
• hide or disable UI elements
|
||||
• prevent navigation under certain conditions
|
||||
|
||||
Blockers MUST:
|
||||
• be reversible
|
||||
• be local to the frontend
|
||||
• be treated as best-effort helpers
|
||||
|
||||
⸻
|
||||
|
||||
Restrictions
|
||||
|
||||
Blockers MUST NOT:
|
||||
• enforce security
|
||||
• claim authorization
|
||||
• block access permanently
|
||||
• replace backend Guards
|
||||
• make assumptions about backend state
|
||||
|
||||
⸻
|
||||
|
||||
Common Frontend Blockers
|
||||
• SubmitBlocker
|
||||
• AuthBlocker
|
||||
• RoleBlocker
|
||||
• ThrottleBlocker
|
||||
• NavigationBlocker
|
||||
• FeatureBlocker
|
||||
|
||||
⸻
|
||||
|
||||
Summary (Frontend)
|
||||
• Blockers prevent execution
|
||||
• Blockers improve UX
|
||||
• Blockers reduce mistakes and load
|
||||
|
||||
⸻
|
||||
|
||||
Clear Separation
|
||||
|
||||
Aspect Blocker (Frontend) Guard (Backend)
|
||||
Purpose Prevent execution Enforce rules
|
||||
Security ❌ No ✅ Yes
|
||||
Authority ❌ Best-effort ✅ Final
|
||||
Reversible ✅ Yes ❌ No
|
||||
Failure effect UI feedback HTTP error
|
||||
|
||||
|
||||
⸻
|
||||
|
||||
Naming Rules (Hard)
|
||||
• Frontend uses *Blocker
|
||||
• Backend uses *Guard
|
||||
• Never mix the terms
|
||||
• Never implement Guards in the frontend
|
||||
• Never implement Blockers in the backend
|
||||
|
||||
⸻
|
||||
|
||||
Final Rule
|
||||
|
||||
If it must be enforced, it is a Guard.
|
||||
|
||||
If it only prevents UX mistakes, it is a Blocker.
|
||||
@@ -50,11 +50,10 @@ Blockers exist to prevent UX mistakes.
|
||||
- Blockers may reduce unnecessary requests.
|
||||
- The API still enforces rules.
|
||||
|
||||
See [`BLOCKER_GUARDS.md`](docs/architecture/website/BLOCKER_GUARDS.md:1).
|
||||
See [`docs/architecture/shared/BLOCKERS_AND_GUARDS.md`](docs/architecture/shared/BLOCKERS_AND_GUARDS.md:1) and [`docs/architecture/website/BLOCKERS.md`](docs/architecture/website/BLOCKERS.md:1).
|
||||
|
||||
## 6) Canonical placement in this repo
|
||||
|
||||
- `apps/website/lib/blockers/**`
|
||||
- `apps/website/lib/hooks/**`
|
||||
- `apps/website/lib/command-models/**`
|
||||
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
# Login Flow State Machine Architecture
|
||||
# Login Flow State Machine (Strict)
|
||||
|
||||
## Problem
|
||||
The current login page has unpredictable behavior due to:
|
||||
- Multiple useEffect runs with different session states
|
||||
- Race conditions between session loading and redirect logic
|
||||
- Client-side redirects that interfere with test expectations
|
||||
This document defines the canonical, deterministic login flow controller for the website.
|
||||
|
||||
## Solution: State Machine Pattern
|
||||
Authoritative website contract:
|
||||
|
||||
### State Definitions
|
||||
- [`docs/architecture/website/WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1)
|
||||
|
||||
## 1) Core rule
|
||||
|
||||
Login flow logic MUST be deterministic.
|
||||
|
||||
The same inputs MUST produce the same state and the same next action.
|
||||
|
||||
## 2) State machine definition (strict)
|
||||
|
||||
### 2.1 State definitions
|
||||
|
||||
```typescript
|
||||
enum LoginState {
|
||||
@@ -19,7 +25,7 @@ enum LoginState {
|
||||
}
|
||||
```
|
||||
|
||||
### State Transition Table
|
||||
### 2.2 State transition table
|
||||
|
||||
| Current State | Session | ReturnTo | Next State | Action |
|
||||
|---------------|---------|----------|------------|--------|
|
||||
@@ -29,7 +35,7 @@ enum LoginState {
|
||||
| UNAUTHENTICATED | exists | any | POST_AUTH_REDIRECT | Redirect to returnTo |
|
||||
| AUTHENTICATED_WITHOUT_PERMISSIONS | exists | any | POST_AUTH_REDIRECT | Redirect to returnTo |
|
||||
|
||||
### Class-Based Controller
|
||||
### 2.3 Class-based controller
|
||||
|
||||
```typescript
|
||||
class LoginFlowController {
|
||||
@@ -57,7 +63,7 @@ class LoginFlowController {
|
||||
return this.state;
|
||||
}
|
||||
|
||||
// Pure function - returns action, doesn't execute
|
||||
// Pure function - returns action, does not execute
|
||||
getNextAction(): LoginAction {
|
||||
switch (this.state) {
|
||||
case LoginState.UNAUTHENTICATED:
|
||||
@@ -71,7 +77,7 @@ class LoginFlowController {
|
||||
}
|
||||
}
|
||||
|
||||
// Called after authentication
|
||||
// Transition called after authentication
|
||||
transitionToPostAuth(): void {
|
||||
if (this.session) {
|
||||
this.state = LoginState.POST_AUTH_REDIRECT;
|
||||
@@ -80,15 +86,14 @@ class LoginFlowController {
|
||||
}
|
||||
```
|
||||
|
||||
### Benefits
|
||||
## 3) Non-negotiable rules
|
||||
|
||||
1. **Predictable**: Same inputs always produce same outputs
|
||||
2. **Testable**: Can test each state transition independently
|
||||
3. **No Race Conditions**: State determined once at construction
|
||||
4. **Clear Intent**: Each state has a single purpose
|
||||
5. **Maintainable**: Easy to add new states or modify transitions
|
||||
1. The controller MUST be constructed from explicit inputs only.
|
||||
2. The controller MUST NOT perform side effects.
|
||||
3. Side effects (routing) MUST be executed outside the controller.
|
||||
4. The controller MUST be unit-tested per transition.
|
||||
|
||||
### Usage in Login Page
|
||||
## 4) Usage in login page (example)
|
||||
|
||||
```typescript
|
||||
export default function LoginPage() {
|
||||
@@ -129,4 +134,4 @@ export default function LoginPage() {
|
||||
}
|
||||
```
|
||||
|
||||
This eliminates all the unpredictable behavior and makes the flow testable and maintainable.
|
||||
This pattern ensures deterministic behavior and makes the flow testable.
|
||||
|
||||
46
docs/architecture/website/WEBSITE_AUTH_FLOW.md
Normal file
46
docs/architecture/website/WEBSITE_AUTH_FLOW.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Authentication UX Flow (Website)
|
||||
|
||||
This document defines how the website handles authentication from a UX perspective.
|
||||
|
||||
Shared contract:
|
||||
|
||||
- [`docs/architecture/shared/AUTH_CONTRACT.md`](docs/architecture/shared/AUTH_CONTRACT.md:1)
|
||||
|
||||
Authoritative website contract:
|
||||
|
||||
- [`docs/architecture/website/WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1)
|
||||
|
||||
## 1) Website role (strict)
|
||||
|
||||
The website:
|
||||
|
||||
- redirects unauthenticated users to login
|
||||
- hides or disables UI based on best-effort session knowledge
|
||||
|
||||
The website does not enforce security.
|
||||
|
||||
## 2) Canonical website flow
|
||||
|
||||
```text
|
||||
Request
|
||||
↓
|
||||
Website routing
|
||||
↓
|
||||
API requests with credentials
|
||||
↓
|
||||
API enforces authentication and authorization
|
||||
↓
|
||||
Website renders result or redirects
|
||||
```
|
||||
|
||||
## 3) Non-negotiable rules
|
||||
|
||||
1. The website MUST NOT claim authorization.
|
||||
2. The website MUST NOT trust client state for enforcement.
|
||||
3. Every write still relies on the API to accept or reject.
|
||||
|
||||
Related:
|
||||
|
||||
- Website blockers: [`docs/architecture/website/BLOCKERS.md`](docs/architecture/website/BLOCKERS.md:1)
|
||||
- Client state rules: [`docs/architecture/website/CLIENT_STATE.md`](docs/architecture/website/CLIENT_STATE.md:1)
|
||||
|
||||
@@ -189,7 +189,7 @@ See [`FORM_SUBMISSION.md`](docs/architecture/website/FORM_SUBMISSION.md:1).
|
||||
- The website MUST NOT enforce security.
|
||||
- The API enforces authentication and authorization.
|
||||
|
||||
See [`BLOCKER_GUARDS.md`](docs/architecture/website/BLOCKER_GUARDS.md:1).
|
||||
See [`docs/architecture/shared/BLOCKERS_AND_GUARDS.md`](docs/architecture/shared/BLOCKERS_AND_GUARDS.md:1) and [`docs/architecture/website/BLOCKERS.md`](docs/architecture/website/BLOCKERS.md:1).
|
||||
|
||||
## 7.1) Client state (strict)
|
||||
|
||||
|
||||
65
docs/architecture/website/WEBSITE_DATA_FLOW.md
Normal file
65
docs/architecture/website/WEBSITE_DATA_FLOW.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Website Data Flow (Strict)
|
||||
|
||||
This document defines the **apps/website** data flow.
|
||||
|
||||
Authoritative contract: [`docs/architecture/website/WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1).
|
||||
|
||||
Website scope:
|
||||
|
||||
- `apps/website/**`
|
||||
|
||||
## 1) Website role
|
||||
|
||||
The website is a **delivery layer**.
|
||||
|
||||
It renders truth from the API and forwards user intent to the API.
|
||||
|
||||
## 2) Read flow
|
||||
|
||||
```text
|
||||
RSC page.tsx
|
||||
↓
|
||||
PageQuery
|
||||
↓
|
||||
API client (infra)
|
||||
↓
|
||||
API Transport DTO
|
||||
↓
|
||||
Page DTO
|
||||
↓
|
||||
Presenter (client)
|
||||
↓
|
||||
ViewModel (optional)
|
||||
↓
|
||||
Presenter (client)
|
||||
↓
|
||||
ViewData
|
||||
↓
|
||||
Template
|
||||
```
|
||||
|
||||
## 3) Write flow
|
||||
|
||||
All writes enter through **Server Actions**.
|
||||
|
||||
```text
|
||||
User intent
|
||||
↓
|
||||
Server Action
|
||||
↓
|
||||
Command Model / Request DTO
|
||||
↓
|
||||
API
|
||||
↓
|
||||
Revalidation
|
||||
↓
|
||||
RSC reload
|
||||
```
|
||||
|
||||
## 4) Non-negotiable rules
|
||||
|
||||
1. Templates accept ViewData only.
|
||||
2. Page Queries do not format.
|
||||
3. Presenters do not call the API.
|
||||
4. Client state is UI-only.
|
||||
|
||||
50
docs/architecture/website/WEBSITE_FILE_STRUCTURE.md
Normal file
50
docs/architecture/website/WEBSITE_FILE_STRUCTURE.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Website File Structure (Strict)
|
||||
|
||||
This document defines the canonical **physical** structure for `apps/website/**`.
|
||||
|
||||
It describes where code lives, not the full behavioral rules.
|
||||
|
||||
Authoritative contract:
|
||||
|
||||
- [`docs/architecture/website/WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1)
|
||||
|
||||
## 1) High-level layout
|
||||
|
||||
```text
|
||||
apps/website/
|
||||
app/ Next.js routes (RSC pages, layouts, server actions)
|
||||
templates/ template components (ViewData only)
|
||||
lib/ website code (clients, services, view-models, etc.)
|
||||
```
|
||||
|
||||
## 2) `apps/website/app/` (routing)
|
||||
|
||||
Routes are implemented via Next.js App Router.
|
||||
|
||||
Rules:
|
||||
|
||||
- server `page.tsx` does composition only
|
||||
- templates are pure
|
||||
- writes enter via server actions
|
||||
|
||||
See [`docs/architecture/website/WEBSITE_RSC_PRESENTATION.md`](docs/architecture/website/WEBSITE_RSC_PRESENTATION.md:1).
|
||||
|
||||
## 3) `apps/website/lib/` (website internals)
|
||||
|
||||
Canonical folders (existing in this repo):
|
||||
|
||||
```text
|
||||
apps/website/lib/
|
||||
api/ API clients
|
||||
infrastructure/ technical concerns
|
||||
services/ UI orchestration (read-only and write orchestration)
|
||||
page-queries/ server composition
|
||||
types/ API transport DTOs
|
||||
view-models/ client-only classes
|
||||
display-objects/ deterministic formatting helpers
|
||||
command-models/ transient form models
|
||||
blockers/ UX-only prevention
|
||||
hooks/ React-only helpers
|
||||
di/ client-first DI integration
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user