119 lines
2.6 KiB
Markdown
119 lines
2.6 KiB
Markdown
# Domain model
|
|
|
|
Use UUID primary keys internally. Human-facing references are immutable unique strings.
|
|
|
|
## User
|
|
|
|
- id
|
|
- public_ref (`USR-...`)
|
|
- display_name
|
|
- role: `operations_manager | rental_employee`
|
|
- active
|
|
|
|
## Customer
|
|
|
|
- id
|
|
- public_ref (`CUS-0001`)
|
|
- first_name, last_name
|
|
- email, phone
|
|
- postal_code, city
|
|
- date_of_birth (optional)
|
|
- merged_into_customer_id (optional)
|
|
- created_at, updated_at
|
|
|
|
## Vehicle
|
|
|
|
- id
|
|
- public_ref (`MO-001`)
|
|
- make, model, model_year
|
|
- registration_number
|
|
- location
|
|
- operational_status: `available | rented | cleaning | maintenance | blocked`
|
|
- odometer_km
|
|
- next_service_km
|
|
- active
|
|
- version for optimistic concurrency
|
|
|
|
## Booking
|
|
|
|
- id
|
|
- public_ref (`BK-...`)
|
|
- customer_id, vehicle_id
|
|
- starts_at, ends_at
|
|
- status: `reserved | active | returned | cancelled | blocked`
|
|
- start_odometer_km, end_odometer_km
|
|
- requirements_complete
|
|
|
|
Database invariant: active/reserved bookings for the same vehicle may not overlap after validation. A deliberate seeded conflict may be imported as an external-data quality issue through a controlled bypass, never created through the normal command API.
|
|
|
|
## Inspection
|
|
|
|
- id
|
|
- public_ref
|
|
- booking_id, vehicle_id
|
|
- type: `checkout | return`
|
|
- fuel_level_percent
|
|
- cleanliness_ok
|
|
- damage_reported
|
|
- technical_warning
|
|
- notes
|
|
- odometer_km
|
|
- completed_at, completed_by
|
|
|
|
## MaintenanceRecord
|
|
|
|
- id
|
|
- vehicle_id
|
|
- occurred_at
|
|
- odometer_km
|
|
- category
|
|
- summary
|
|
|
|
## DataQualityIssue
|
|
|
|
- id
|
|
- public_ref
|
|
- rule_type:
|
|
- `possible_duplicate_customer`
|
|
- `missing_required_field`
|
|
- `odometer_regression`
|
|
- `booking_overlap`
|
|
- `vehicle_status_conflict`
|
|
- entity_type, entity_id
|
|
- severity: `low | medium | high`
|
|
- status: `open | deferred | resolved | rejected`
|
|
- evidence_json
|
|
- proposed_action_json
|
|
- detected_at, resolved_at, resolved_by
|
|
|
|
## OutboxEvent / WorkflowRun
|
|
|
|
- id / event_id
|
|
- event_type
|
|
- aggregate_type, aggregate_id
|
|
- payload_json
|
|
- occurred_at
|
|
- delivery_status: `pending | delivering | succeeded | failed`
|
|
- attempts, next_attempt_at, last_error
|
|
- external_run_id
|
|
|
|
## AuditEvent
|
|
|
|
- id
|
|
- actor_type: `user | service | system`
|
|
- actor_id / actor_label
|
|
- action
|
|
- entity_type, entity_id
|
|
- correlation_id
|
|
- before_json, after_json, metadata_json
|
|
- occurred_at
|
|
|
|
## Required invariants
|
|
|
|
- a vehicle return cannot reduce the canonical odometer;
|
|
- a return with a lower submitted reading is recorded as an inspection and issue, while canonical odometer remains unchanged;
|
|
- damage or a critical warning blocks the vehicle;
|
|
- merged customers remain as tombstones linked to the survivor;
|
|
- resolving a quality issue and applying its correction is transactional and audited;
|
|
- public references never change after creation.
|