Files
core/bible-local/architecture/data-model.md
2026-03-15 21:38:20 +03:00

130 lines
5.6 KiB
Markdown

# Data Model And Migration Boundaries
## Naming Model
- Internal canonical naming uses `Machine` and `Part`.
- API/UI compatibility keeps `asset` and `component` terminology where already exposed.
- In domain aliases:
- `type Asset = Machine`
- `type Component = Part`
## Core Persistence Areas
- Asset registry: `machines`
- Component registry: `parts`
- Installations: `installations`
- Observations: `observations`
- Timeline: `timeline_events`
- Failures: `failure_events`
- Asset firmware state: `machine_firmware_states`
- Asset sensor readings: `machine_sensor_readings`
- Asset logs: `machine_event_logs`
- Component history (canonical): `component_change_events`, `component_state_snapshots`
- Asset history (canonical): `asset_change_events`, `asset_state_snapshots`
- History jobs/audit: `history_recompute_jobs`, `history_admin_audit`
## Sensor Readings
`machine_sensor_readings` is a last-known-value projection for hardware sensors per asset.
- Primary key: `(machine_id, sensor_type, sensor_name)`.
- `sensor_type` is one of: `fan`, `power`, `temperature`, `other`.
- `value_json` stores type-specific numeric readings (see JSON import contract).
- Updated on every ingest — not history-backed (no events, no snapshots).
- `collected_at` reflects the source observation time, not ingest time.
## Asset Event Logs
`machine_event_logs` is a deduplicated append/update read model for host/BMC/Redfish logs per asset.
- Scope: operational log entries shown in dedicated asset log views, not timeline/history.
- Logical identity is a normalized log fingerprint, not the raw ingest row.
- Required stored dimensions:
- `machine_id`
- `log_source` (`host`, `bmc`, `redfish`)
- `fingerprint`
- `event_time`
- `severity`
- `message_id`
- `message`
- `component_ref`
- `raw_payload_json`
- `first_seen_at`
- `last_seen_at`
- `seen_count`
- Upsert rule:
- new fingerprint for same asset/source -> insert new log row
- repeated fingerprint -> update `last_seen_at`, increment `seen_count`, refresh mutable presentation fields if needed
- Not history-backed:
- no `asset_change_events`
- no `component_change_events`
- no `timeline_events`
- `raw_payload_json` is retained for diagnostics and vendor-specific fields.
Out of active model:
- `projects` is legacy and not used by runtime handlers/contracts.
## Migration Policy
- Migrations are stored in `migrations/`.
- Schema migrations are applied at startup.
- Legacy entities removed in migration `0016_remove_legacy_features` remain out of active scope.
- History architecture was introduced in migration `0017_history_core`.
- Timeline UI/filter denormalized facets were added in migration `0018_timeline_ui_facets`.
- Installation slot/location (`installations.slot_name`) was added in migration `0019_installation_slot_name`.
- History-first `component_type` projection/storage backfill was introduced in migration `0020_component_type_history_first`.
## Canonical State Model (History-First)
- `parts` and `machines` are current-state projections optimized for reads.
- `component_change_events` / `asset_change_events` are canonical state-change logs.
- `component_state_snapshots` / `asset_state_snapshots` store full after-state snapshots for each state-changing event.
- `timeline_events`, `installations`, `failure_events`, and `machine_firmware_states` are projections and may be rebuilt from history.
- `observations` is a raw ingest trace/audit store and must not be used as source-of-truth for current component/asset state in runtime UI/API flows.
- `parts.component_type` and `installations.slot_name` are the runtime-readable projections for component type and current slot/location (not `observations.details`).
## History Event Storage Rules
- History events are versioned per entity (`version` is monotonic per component/asset).
- State-changing writes persist:
1. change event
2. after-state snapshot
3. projection updates (`parts` / `machines` and related projections)
4. timeline projection rows
- History event deletion is soft-delete (`is_deleted`) for normal user/admin flow.
- Hard restore is destructive for future history rows and is audited in `history_admin_audit`.
## Timeline Projection Linkage
`timeline_events` includes logical linkage back to history:
- `logical_entity_type`
- `logical_event_id`
- `correlation_id`
- `is_deleted`
This linkage is required for recompute, rollback/delete propagation, and paired asset/component timeline rendering.
`timeline_events` also stores denormalized UI/filter facets (projection-only, rebuildable from history + registry state), including:
- `source_type`
- `visual_action`
- `component_serial`
- `component_model` (used as v1 `part_number`)
- `asset_serial`
- `asset_name_cached`
- `slot_name`
- `device_name`
- `changed_fields_json`
These fields are not canonical state and must be treated as timeline projection helpers only.
## Installation Slot (Current Components UI)
- `installations.slot_name` is the canonical editable slot/location for a component attached to a server.
- Asset page `Current Components` bulk actions (`Add / Edit / Remove`) read/write slot through history-driven component installation state (`installation.slot_name` in component snapshots) and synchronize projection `installations.slot_name`.
- Runtime invariant (app-level validation):
- for the same `machine_id`, open installations (`removed_at IS NULL`) must not share the same non-empty `slot_name`.
- Projection-update nuance:
- slot uniqueness validation must only block writes when `slot_name` is actually changing for the installation row (status-only edits must not fail because of pre-existing duplicate slot data).