Files
gridpilot.gg/packages/shared/docs/ValueObjectCandidates.md
2025-12-11 13:50:38 +01:00

18 KiB
Raw Blame History

Value Object Candidates Audit

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.
  • Low: Niche or rarely used concepts.

Analytics

Analytics/PageView

  • Concept: PageViewId Implemented

    • Implementation: PageViewId, PageView, PageViewId.test
    • 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.
    • Priority: High
  • Concept: AnalyticsEntityId (for analytics) Implemented

  • Concept: AnalyticsSessionId Implemented

  • Concept: ReferrerUrl

    • Location: PageView.referrer, PageViewProps.referrer
    • Why VO: External URL with semantics around internal vs external (isExternalReferral method). Currently string with no URL parsing or normalization.
    • Priority: Medium
  • Concept: CountryCode

  • Concept: SnapshotId

  • Concept: SnapshotPeriod (as VO vs string union)

Analytics/EngagementEvent

  • Concept: EngagementEventId

  • Concept: ActorId (analytics)

    • Location: EngagementEvent.actorId, EngagementEventProps.actorId
    • 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)

Notifications

Notification Entity

NotificationPreference Entity


Media

AvatarGenerationRequest


Identity

SponsorAccount


Racing

League Entity

  • Concept: LeagueId

    • Location: League.id, validate ID check in League.validate
    • 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, validation in League.validate
    • Why VO: Identity of league owner; likely maps to a UserId or DriverId concept; should not remain a free-form string.
    • Priority: High
  • Concept: LeagueSocialLinkUrl (DiscordUrl, YoutubeUrl, WebsiteUrl)

Track Entity

  • Concept: TrackId

    • Location: Track.id, validation in Track.validate
    • Why VO: Aggregate root ID for tracks; referenced from races and schedules; currently primitive string.
    • Priority: High
  • Concept: TrackCountryCode

    • Location: Track.country, validation in Track.validate
    • Why VO: Represent country using standard codes; currently a free-form string.
    • Priority: Medium
  • Concept: TrackImageUrl

    • Location: Track.imageUrl
    • Why VO: Image asset URL; should be constrained and validated similarly to other URL concepts.
    • Priority: High
  • Concept: GameId

    • Location: Track.gameId, validation in Track.validate
    • Why VO: Identifier for simulation/game platform; currently string with non-empty validation; may benefit from VO if multiple entities use it.
    • Priority: Medium

Race Entity

  • Concept: RaceId

    • Location: Race.id, validation in Race.validate
    • Why VO: Aggregate ID for races; central to many operations and references.
    • Priority: High
  • Concept: RaceLeagueId

    • Location: Race.leagueId, validation in Race.validate
    • Why VO: Foreign key into League; should be modeled as LeagueId VO rather than raw string.
    • Priority: High
  • Concept: RaceTrackId / RaceCarId

    • Location: Race.trackId, Race.carId
    • Why VO: Optional references to track and car entities; currently strings; could be typed IDs aligned with TrackId and car ID concepts.
    • Priority: Medium
  • Concept: RaceName / TrackName / CarName

    • Location: Race.track, Race.car
    • Why VO: Displayable names with potential formatting rules; today treated as raw strings, which is acceptable for now.
    • Priority: Low

Team Entity

  • Concept: TeamId

    • Location: Team.id, validation in Team.validate
    • Why VO: Aggregate ID; referenced from standings, registrations, etc. Currently primitive.
    • Priority: High
  • Concept: TeamOwnerId

    • Location: Team.ownerId, validation in Team.validate
    • Why VO: Identity of team owner; should map to UserId or DriverId, currently a simple string.
    • Priority: High
  • Concept: TeamLeagueId (for membership list)

    • Location: Team.leagues, validation in Team.validate
    • 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.