3.1 KiB
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.