174 lines
3.1 KiB
Markdown
174 lines
3.1 KiB
Markdown
Form Submission Flow (UI → System)
|
|
|
|
This document defines the only valid data flow when a user submits a form.
|
|
It applies to all write operations (create, update, delete).
|
|
|
|
There are no exceptions.
|
|
|
|
Authoritative contract: [`WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1).
|
|
|
|
⸻
|
|
|
|
Core Principle
|
|
|
|
Read and Write paths are different.
|
|
|
|
What is displayed is never sent back.
|
|
|
|
## Non-negotiable write boundary
|
|
|
|
All writes MUST enter the system through **Next.js Server Actions**.
|
|
|
|
Forbidden:
|
|
|
|
- client components performing write HTTP requests
|
|
- client components calling API clients for mutations
|
|
|
|
Allowed:
|
|
|
|
- client submits intent (FormData, button action)
|
|
- server action performs UX validation
|
|
- server action calls the API
|
|
|
|
⸻
|
|
|
|
High-Level Flow
|
|
|
|
UI → Command DTO → API → Core Use Case → Persistence
|
|
|
|
• View Models are read-only
|
|
• Display Objects are read-only
|
|
• Commands are write-only
|
|
|
|
⸻
|
|
|
|
1. UI (Component)
|
|
|
|
Responsibility
|
|
• Collect user input
|
|
• Manage UX state (loading, disabled, local errors)
|
|
|
|
Rules
|
|
• Only primitives are handled (string, number, boolean)
|
|
• No DTO reuse
|
|
• No ViewModel reuse
|
|
• No domain objects
|
|
|
|
The UI does not decide whether an action is allowed.
|
|
|
|
⸻
|
|
|
|
2. Form Model (Optional)
|
|
|
|
Responsibility
|
|
• Local form state
|
|
• Client-side validation (required, min/max length)
|
|
• Field-level errors
|
|
|
|
Rules
|
|
• UX-only validation
|
|
• No business rules
|
|
• Never shared with API or Core
|
|
|
|
⸻
|
|
|
|
3. Command DTO (Frontend)
|
|
|
|
Responsibility
|
|
• Express intent
|
|
• Represent a write operation
|
|
|
|
Rules
|
|
• Created fresh on submit
|
|
• Never derived from a ViewModel
|
|
• Never reused from read DTOs
|
|
• Write-only
|
|
|
|
⸻
|
|
|
|
4. Frontend Service
|
|
|
|
Responsibility
|
|
• Orchestrate the write action
|
|
• Call the API Client
|
|
• Propagate success or failure
|
|
|
|
Rules
|
|
• No business logic
|
|
• No validation
|
|
• No UI decisions
|
|
• No ViewModel creation for writes (except explicit success summaries)
|
|
|
|
⸻
|
|
|
|
5. API Client (Frontend)
|
|
|
|
Responsibility
|
|
• Perform HTTP request
|
|
• Handle transport-level failures
|
|
|
|
Rules
|
|
• Stateless
|
|
• No retries unless explicitly designed
|
|
• Throws technical errors only
|
|
|
|
⸻
|
|
|
|
6. API Layer (Backend)
|
|
|
|
Responsibility
|
|
• HTTP boundary
|
|
• Transport validation (schema / class-validator)
|
|
• Map API DTO → Core Command
|
|
|
|
Rules
|
|
• No business logic
|
|
• No persistence
|
|
• No UI concerns
|
|
|
|
⸻
|
|
|
|
7. Core Use Case
|
|
|
|
Responsibility
|
|
• Enforce business rules
|
|
• Validate domain invariants
|
|
• Change system state
|
|
|
|
Rules
|
|
• Single source of truth
|
|
• No UI logic
|
|
• No HTTP knowledge
|
|
|
|
⸻
|
|
|
|
Response Handling
|
|
|
|
Success
|
|
• API returns a Result DTO (IDs, status)
|
|
• Frontend reacts by:
|
|
• navigation
|
|
• reload via GET
|
|
• toast / confirmation
|
|
|
|
Failure
|
|
• Business errors → user-visible message
|
|
• Technical errors → error boundary / monitoring
|
|
|
|
⸻
|
|
|
|
Forbidden Patterns
|
|
• ViewModel → Command
|
|
• DisplayObject → API
|
|
• DTO roundtrip
|
|
• Domain Object in UI
|
|
• Reusing read models for writes
|
|
|
|
⸻
|
|
|
|
Summary
|
|
• Read Flow: DTO → ViewModel → UI
|
|
• Write Flow: UI → Command DTO → Core
|
|
|
|
What is shown is never sent back.
|