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.