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