--- description: All status data must use the Status value object; extend CORE_STATUSES via allowed list — do not invent parallel helpers alwaysApply: true --- # Status Value Object ## Mandatory ALL status fields on primary entities in the domain and application layers MUST use `Status` from `src/common/value-objects/status/`. - Construct via `Status.create(raw)` (core set) or `Status.create(raw, allowed)` (module extensions) - Default new records with `Status.DEFAULT` (`draft`) when status is omitted - Compare with `equals()`, serialize with `value` / `toString()` / `toJSON()` - Persist and transmit the canonical string from `status.value` Core statuses (source of truth): `draft`, `active`, `archived`. Modules may pass an `allowed` list that adds values (e.g. `in_transit`). ## Forbidden Do NOT: - Store or pass status as an unvalidated plain `string` in domain models or services (beyond the DTO/HTTP or DB string boundary) - Add status enums, validators, maps, or helpers that bypass the VO - Create new files for status validation or normalization - Echo raw invalid input in error messages ```typescript // BAD user.status = 'Draft' function normalizeStatus(raw: string): string { /* ... */ } // GOOD const status = Status.create(dto.status ?? Status.DEFAULT) entity.status = status await repo.save({ status: status.value }) // GOOD — module extension const status = Status.create(dto.status, [...CORE_STATUSES, 'in_transit']) ``` ## When the VO is not enough 1. **Update** `src/common/value-objects/status/` (implementation + colocated tests), or pass a wider `allowed` list at the call site 2. Do **not** invent a parallel status type or module ## Boundaries - HTTP DTOs may accept `string`; map to `Status.create()` at the service boundary - Database columns may store `text` (default `'draft'`); map to/from `Status` in the repository - `InvalidStatusError` is the only status validation error; do not echo raw input in messages