# 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).