website refactor

This commit is contained in:
2026-01-13 02:42:58 +01:00
parent 38b25bafe1
commit b82736b24b
6 changed files with 661 additions and 251 deletions

View File

@@ -1,108 +1,64 @@
Command Models
This document defines Command Models as a first-class concept in the frontend architecture.
Command Models are UX-only write models used to collect, validate, and prepare user input
before it is sent to the backend as a Command DTO.
This document defines Command Models as a concept for frontend form handling.
Command Models are not View Models and not Domain Models.
**IMPORTANT**: Command Models are **optional UX helpers**. They are NOT enforced by ESLint rules and do not belong in the strict architecture contract.
## 1) Definition
Purpose
Command Models (also called Form Models) are UX-only write models used to:
- Collect user input
- Track form state (dirty, touched, submitting)
- Perform basic UX validation
- Build Command DTOs for submission
A Form Model answers the question:
**Command Models are NOT:**
- Domain models
- View models
- Security boundaries
- Required for the architecture
“What does the UI need in order to safely submit user input?”
## 2) Purpose
Command Models exist to:
• centralize form state
• reduce logic inside components
• provide consistent client-side validation
• build Command DTOs explicitly
Use Command Models when:
- Forms have complex state management
- Multiple fields need validation
- You want to centralize form logic
- Building DTOs is non-trivial
**Don't use Command Models when:**
- Forms are simple (use React state directly)
- You're building a quick prototype
- The form logic is trivial
Core Rules
## 3) Core Rules
Command Models:
• exist only in the frontend
• are write-only (never reused for reads)
• are created per form
• are discarded after submission
If you use Command Models:
Command Models MUST NOT:
• contain business logic
• enforce domain rules
• reference View Models
• reference Domain Entities or Value Objects
• be sent to the API directly
**They MUST:**
- Live in `components/` or `hooks/` (not `lib/`)
- Be write-only (never reused for reads)
- Be discarded after submission
- Only perform UX validation
**They MUST NOT:**
- Contain business logic
- Enforce domain rules
- Reference View Models or Domain Entities
- Be sent to the API directly (use `toCommand()`)
Relationship to Other Models
API DTO (read) → ViewModel → UI
UI Input → FormModel → Command DTO → API
• View Models are read-only
• Command Models are write-only
• No model is reused across read/write boundaries
Typical Responsibilities
A Form Model MAY:
• store field values
• track dirty / touched state
• perform basic UX validation
• expose isValid, canSubmit
• build a Command DTO
A Form Model MUST NOT:
• decide if an action is allowed
• perform authorization checks
• validate cross-aggregate rules
Validation Guidelines
Client-side validation is UX validation, not business validation.
Allowed validation examples:
• required fields
• min / max length
• email format
• numeric ranges
Forbidden validation examples:
• “user is not allowed”
• “league already exists”
• “quota exceeded”
Server validation is the source of truth.
Example: Simple Form Model (with class-validator)
import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';
## 4) Example
```typescript
// In your component file or hooks/
export class SignupFormModel {
@IsEmail()
email = '';
@IsNotEmpty()
@MinLength(8)
password = '';
isSubmitting = false;
reset(): void {
this.email = '';
this.password = '';
isValid(): boolean {
// UX validation only
return this.email.includes('@') && this.password.length >= 8;
}
toCommand(): SignupCommandDto {
@@ -113,44 +69,51 @@ export class SignupFormModel {
}
}
// Usage
export function SignupForm() {
const [form] = useState(() => new SignupFormModel());
async function handleSubmit() {
if (!form.isValid()) return;
form.isSubmitting = true;
const result = await signupMutation.mutateAsync(form.toCommand());
form.isSubmitting = false;
}
Usage in UI Component
const form = useFormModel(SignupFormModel);
async function onSubmit() {
if (!form.isValid()) return;
form.isSubmitting = true;
await authService.signup(form.toCommand());
return (
<form onSubmit={handleSubmit}>
<input value={form.email} onChange={e => form.email = e.target.value} />
{/* ... */}
</form>
);
}
```
The component:
• binds inputs to the Form Model
• reacts to validation state
• never builds DTOs manually
## 5) Key Principle
**Command Models are optional.** The backend must validate everything.
Testing
If you don't use Command Models, that's fine! Just:
- Use React state for form data
- Let the backend handle validation
- Return clear errors from mutations
Command Models SHOULD be tested when they contain:
• validation rules
• non-trivial state transitions
• command construction logic
## 6) Comparison
Command Models do NOT need tests if they only hold fields without logic.
| Approach | When to Use | Where |
|----------|-------------|-------|
| **React State** | Simple forms, prototypes | Component |
| **Command Model** | Complex forms, multi-step | Component/Hook |
| **View Model** | Read-only UI state | `lib/view-models/` |
| **Service** | Business orchestration | `lib/services/` |
## 7) Summary
Summary
• Command Models are UX helpers for writes
• They protect components from complexity
• They never replace backend validation
• They never leak into read flows
Command Models are **optional UX sugar**. They:
- Help organize complex forms
- Are NOT required by the architecture
- Don't need ESLint enforcement
- Should stay in `components/` or `hooks/`
Command Models help users.
Use Cases protect the system.
Use them if they make your life easier. Skip them if they don't.