Files
gridpilot.gg/docs/architecture/website/COMMAND_MODELS.md
2026-01-11 13:04:33 +01:00

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.