2.6 KiB
2.6 KiB
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_customermissing_required_fieldodometer_regressionbooking_overlapvehicle_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.