Files
gridpilot.gg/docs/architecture/website/COMMAND_MODELS.md
2026-01-13 02:42:58 +01:00

2.9 KiB

Command Models

This document defines Command Models as a concept for frontend form handling.

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

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

Command Models are NOT:

  • Domain models
  • View models
  • Security boundaries
  • Required for the architecture

2) Purpose

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

3) Core Rules

If you use Command Models:

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())

4) Example

// In your component file or hooks/
export class SignupFormModel {
  email = '';
  password = '';
  isSubmitting = false;

  isValid(): boolean {
    // UX validation only
    return this.email.includes('@') && this.password.length >= 8;
  }

  toCommand(): SignupCommandDto {
    return {
      email: this.email,
      password: this.password,
    };
  }
}

// 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;
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={form.email} onChange={e => form.email = e.target.value} />
      {/* ... */}
    </form>
  );
}

5) Key Principle

Command Models are optional. The backend must validate everything.

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

6) Comparison

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

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/

Use them if they make your life easier. Skip them if they don't.