Files
gridpilot.gg/docs/architecture/website/FORMATTERS.md
2026-01-24 01:22:43 +01:00

87 lines
3.2 KiB
Markdown

# Displays & Formatters
## Definition
A **Display** encapsulates **reusable, UI-only display logic**.
In this codebase, we distinguish between **Formatters** (Stateless Logic) and **Display Objects** (Rich Value Objects).
### 1. Formatters (The "Mouth")
Formatters are pure, stateless utilities. They are the "Experts" on how to transform a raw value into a primitive string/number.
- **Usage:** Used by `ViewDataBuilders` (Server) and `ViewModels` (Client).
- **Output:** MUST return primitive values only (`string`, `number`, `boolean`, `null`).
- **Uncle Bob says:** "Data structures (ViewData) should not have behavior. Keep logic in stateless utilities."
### 2. Display Objects (The "Rich API")
Display Objects are logic-rich **Value Objects** that live only on the client. They wrap data and provide multiple ways to look at it.
- **Usage:** Used by `ViewModels` (Client) to provide a rich API to the UI.
- **Output:** Can return complex objects or variants.
- **Uncle Bob says:** "Objects expose behavior, not data. Use them to hide the complexity of the UI."
---
## Responsibilities
A Display/Formatter MAY:
- format values (money, dates, durations)
- handle deterministic localization (mapping stable codes to labels)
- encapsulate UI display conventions
A Display/Formatter MUST:
- be deterministic
- be side-effect free
- be implemented as a **class** with static methods (Formatters) or as immutable classes (Display Objects)
---
## Restrictions
A Display/Formatter MUST NOT:
- contain business logic (e.g., "Team is full if count > 10")
- enforce domain invariants
- perform validation
- **be serialized** (only their primitive outputs are stored in `ViewData`)
- call `Intl.*` or `toLocale*` (unless explicitly marked for client-only ViewModels)
---
## Relationship to ViewData and ViewModels
### The "Primitive Compact" (Server-Side)
`ViewDataBuilders` MUST use **Formatters** to produce flat, serializable `ViewData`.
- **Rule:** `ViewData` properties assigned from a Display/Formatter MUST be primitives.
- **Reason:** Ensures `ViewData` remains a "dumb" JSON structure for SSR.
### The "Rich API" (Client-Side)
`ViewModels` MAY use **Display Objects** to provide interactive formatting.
- **Rule:** `ViewModels` can return `Display Object` instances to the UI.
- **Reason:** Allows the UI to access multiple variants (e.g., `date.short`, `date.relative`) without re-fetching data.
---
## Summary of the Flow
```mermaid
graph TD
DTO[Raw DTO] -->|ViewDataBuilder| VD[ViewData]
subgraph "Server: The Formatter Compact"
VD -->|Uses| F[Stateless Formatter]
F -->|Returns| S[Primitive string/number]
end
VD -->|SSR Boundary| T[Template]
subgraph "Client: The DisplayObject Richness"
T -->|Props| CW[ClientWrapper]
CW -->|new| VM[ViewModel]
VM -->|Wraps| DO[Rich Display Object]
DO -->|Provides| R[Rich API: .time, .relative, .date]
end
```
## Final Rule: Where does logic live?
1. **Is it a business rule?** (e.g., "Can join?") → **ViewModel**.
2. **Is it a formatting rule?** (e.g., "How to show date?") → **Formatter/Display**.
3. **Is it for SEO/SSR?****ViewDataBuilder** (using a Formatter).
4. **Is it for interaction?****ViewModel** (using a Display Object).