do to formatters
This commit is contained in:
87
docs/architecture/website/FORMATTERS.md
Normal file
87
docs/architecture/website/FORMATTERS.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user