156 lines
3.1 KiB
Markdown
156 lines
3.1 KiB
Markdown
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.
|
|
|
|
Command Models are not View Models and not Domain Models.
|
|
|
|
⸻
|
|
|
|
Purpose
|
|
|
|
A Form Model answers the question:
|
|
|
|
“What does the UI need in order to safely submit user input?”
|
|
|
|
Command Models exist to:
|
|
• centralize form state
|
|
• reduce logic inside components
|
|
• provide consistent client-side validation
|
|
• build Command DTOs explicitly
|
|
|
|
⸻
|
|
|
|
Core Rules
|
|
|
|
Command Models:
|
|
• exist only in the frontend
|
|
• are write-only (never reused for reads)
|
|
• are created per form
|
|
• are discarded after submission
|
|
|
|
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
|
|
|
|
⸻
|
|
|
|
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';
|
|
|
|
export class SignupFormModel {
|
|
@IsEmail()
|
|
email = '';
|
|
|
|
@IsNotEmpty()
|
|
@MinLength(8)
|
|
password = '';
|
|
|
|
isSubmitting = false;
|
|
|
|
reset(): void {
|
|
this.email = '';
|
|
this.password = '';
|
|
}
|
|
|
|
toCommand(): SignupCommandDto {
|
|
return {
|
|
email: this.email,
|
|
password: this.password,
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
⸻
|
|
|
|
Usage in UI Component
|
|
|
|
const form = useFormModel(SignupFormModel);
|
|
|
|
async function onSubmit() {
|
|
if (!form.isValid()) return;
|
|
|
|
form.isSubmitting = true;
|
|
|
|
await authService.signup(form.toCommand());
|
|
}
|
|
|
|
The component:
|
|
• binds inputs to the Form Model
|
|
• reacts to validation state
|
|
• never builds DTOs manually
|
|
|
|
⸻
|
|
|
|
Testing
|
|
|
|
Command Models SHOULD be tested when they contain:
|
|
• validation rules
|
|
• non-trivial state transitions
|
|
• command construction logic
|
|
|
|
Command Models do NOT need tests if they only hold fields without logic.
|
|
|
|
⸻
|
|
|
|
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 help users.
|
|
Use Cases protect the system. |