# Data Model: Shared Diff Presentation Foundation ## Overview This feature adds presentation-layer value objects and support types only. No database schema or persisted business record changes are introduced. ## Entities ### 1. DiffRowStatus - **Type**: PHP backed enum - **Purpose**: Canonical presentation-state vocabulary for shared diff rendering - **Values**: - `unchanged` - `changed` - `added` - `removed` - **Rules**: - Treated as a presentation concern only - Drives summary counts, badge semantics, icons, and row-state rendering - Must remain reusable outside any single resource or domain service ### 2. DiffRow - **Type**: Immutable value object / DTO - **Purpose**: Render-ready representation of one compare row - **Fields**: - `key`: string - `label`: string - `status`: `DiffRowStatus` - `oldValue`: mixed - `newValue`: mixed - `isListLike`: bool - `addedItems`: array - `removedItems`: array - `unchangedItems`: array - `meta`: array - **Validation rules**: - `key` must be non-empty - `label` must be non-empty - `status` must be one of the enum values - List fragment arrays must be present as empty arrays when not used - `meta` must stay presentation-safe and serializable for view consumption ### 3. DiffSummary - **Type**: Immutable value object / DTO - **Purpose**: Shared summary counts and high-level message context for a rendered compare block - **Fields**: - `changedCount`: int - `addedCount`: int - `removedCount`: int - `unchangedCount`: int - `hasRows`: bool - `message`: ?string - **Validation rules**: - Counts are non-negative integers - `hasRows` is derived from total count - `message` is optional and consumer-supplied or presenter-generated fallback copy ### 4. DiffPresentation - **Type**: Immutable value object / DTO - **Purpose**: Stable wrapper returned by `DiffPresenter` so consumers can pass one object containing summary and ordered rows into shared partials or downstream adapters - **Fields**: - `summary`: `DiffSummary` - `rows`: array - **Validation rules**: - `summary` must always be present - `rows` must preserve deterministic ordering - Empty compares are represented as an empty `rows` collection plus an explicit summary state ### 5. StructuredCompareInput - **Type**: Internal input shape consumed by `DiffPresenter` - **Purpose**: Minimal adaptation contract for simple compare payloads - **Fields**: - `baseline`: array - `current`: array - `changedKeys`: array - `labels`: array - `meta`: array> - **Rules**: - `baseline` and `current` may each omit keys that exist on the other side - `changedKeys` is optional hint data; presenter can still derive state from values - `labels` is optional and must not block rendering when absent ### 6. InlineListFragments - **Type**: Derived presentation fragment embedded in `DiffRow` - **Purpose**: Simple inline list diff rendering for string or scalar lists - **Fields**: - `addedItems`: array - `removedItems`: array - `unchangedItems`: array - **Rules**: - Built only when both values are simple list-like collections appropriate for inline comparison - Ordering should remain deterministic for stable rendering and tests - Not intended for token-level or line-level diff logic ## Relationships - One `DiffSummary` describes a collection of `DiffRow` instances. - Each `DiffRow` has exactly one `DiffRowStatus`. - `DiffPresenter` converts one `StructuredCompareInput` into one `DiffPresentation`, containing one `DiffSummary` plus an ordered list of `DiffRow` instances. - `ValueStringifier` formats `DiffRow.oldValue`, `DiffRow.newValue`, and inline list items for display, but can also be used independently by specialized views. ## State transitions ### Row classification - Missing in baseline + present in current → `added` - Present in baseline + missing in current → `removed` - Present on both sides + materially equal → `unchanged` - Present on both sides + materially different → `changed` ### Summary derivation - Summary counts are derived from the final `DiffRow` list. - Empty compare input produces either an empty summary or a no-data state, but never synthetic changes. ## Non-persistent boundaries - None of these entities map to database tables. - None of these entities may fetch models or rely on Livewire runtime state. - None of these entities define authoritative business drift, compare, or restore logic.