This document lists domain concepts currently modeled as primitives or simple types that should be refactored into explicit value objects implementing `IValueObject<Props>`.
Priority levels:
- **High**: Cross-cutting identifiers, URLs, or settings with clear invariants and repeated usage.
- **Medium**: Important within a single bounded context but less cross-cutting.
- **Notes**: Page view identifiers are now modeled as a VO and used internally by the `PageView` entity while repositories and use cases continue to work with primitive string IDs where appropriate.
- **Notes**: Entity IDs within the analytics bounded context are now modeled as a VO and used internally in snapshots, engagement events, and page views; external DTOs still expose primitive strings.
- **Notes**: Session identifiers are now encapsulated in a VO and used internally across analytics entities while preserving primitive session IDs at the boundaries.
- **Why VO**: External URL with semantics around internal vs external (`isExternalReferral` method). Currently string with no URL parsing or normalization.
- **Why VO**: Has semantics used in [`getPeriodLabel`](packages/analytics/domain/entities/AnalyticsSnapshot.ts:130); could encapsulate formatting logic and date range constraints. Currently a union type only.
- **Priority**: Low (enum-like, acceptable as-is for now)
- **Why VO**: Identifies the actor (anonymous / driver / sponsor) with a type discriminator; could be a specific `ActorId` VO constrained by `actorType`.
- **Priority**: Low (usage seems optional and less central)
- **Notes**: Notification aggregate IDs are now modeled as a VO and used internally by the `Notification` entity; repositories and use cases still operate with primitive string IDs via entity factories and serialization.
- **Why VO**: Encapsulates a time window invariant (0–23, wrap-around support, comparison with current hour); currently implemented as two numbers plus logic in the entity. Ideal VO candidate.
- **Status**: Validation uses [`EmailAddress` VO utilities](packages/identity/domain/value-objects/EmailAddress.ts:15) but the entity still stores `email: string`.
- **Why VO**: Entity should likely store `EmailAddress` instead of a plain string to guarantee invariants wherever it is used.
- **Why VO**: Represents sponsor company name with potential invariants (length, prohibited characters). Currently only checked for non-empty.
- **Priority**: Low
---
## Racing
### League Entity
- **Concept**: `LeagueId`
- **Location**: [`League.id`](packages/racing/domain/entities/League.ts:83), `validate` ID check in [`League.validate`](packages/racing/domain/entities/League.ts:157)
- **Why VO**: Aggregate root ID; central to many references (races, teams, sponsorships). Currently primitive string with non-empty validation only.
- **Priority**: High
- **Concept**: `LeagueOwnerId`
- **Location**: [`League.ownerId`](packages/racing/domain/entities/League.ts:87), validation in [`League.validate`](packages/racing/domain/entities/League.ts:179)
- **Why VO**: Identity of league owner; likely maps to a `UserId` or `DriverId` concept; should not remain a free-form string.
- **Location**: [`Team.leagues`](packages/racing/domain/entities/Team.ts:18), validation in [`Team.validate`](packages/racing/domain/entities/Team.ts:124)
- **Why VO**: Array of league IDs; currently `string[]` with no per-item validation; could leverage `LeagueId` VO and a small collection abstraction.
- **Priority**: Medium
---
## Summary of Highest-Impact Candidates (Not Yet Refactored)
The following are **high-priority** candidates that have not been refactored in this pass but are strong future VO targets:
-`LeagueId`, `RaceId`, `TeamId`, and their foreign key counterparts (`RaceLeagueId`, `RaceTrackId`, `RaceCarId`, `TeamLeagueId`).
- Cross-bounded-context identifiers: `SponsorId` in identity linking to racing `Sponsor`, `PreferenceOwnerId` / `NotificationPreferenceId` in notifications, and remaining analytics/session identifiers where primitive usage persists across boundaries.
- URL-related concepts beyond those refactored in this pass: `LeagueSocialLinkUrl` variants, `TrackImageUrl`, `ReferrerUrl`, `ActionUrl` in notifications, and avatar-related URLs in media (where not yet wrapped).
- Time-window and scheduling primitives: `QuietHours` numeric start/end in notifications, and other time-related raw numbers in stewarding settings and session configuration where richer semantics may help.
These should be considered for future VO-focused refactors once the impact on mappers, repositories, and application layers is planned and coordinated.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.