/** * Domain Entity: Track * * Represents a racing track/circuit in the GridPilot platform. * Immutable entity with factory methods and domain validation. */ import { RacingDomainValidationError } from '../errors/RacingDomainError'; import type { IEntity } from '@core/shared/domain'; import { TrackName } from '../value-objects/TrackName'; import { TrackShortName } from '../value-objects/TrackShortName'; import { TrackCountry } from '../value-objects/TrackCountry'; import { TrackLength } from '../value-objects/TrackLength'; import { TrackTurns } from '../value-objects/TrackTurns'; import { TrackGameId } from '../value-objects/TrackGameId'; import { TrackImageUrl } from '../value-objects/TrackImageUrl'; export type TrackCategory = 'oval' | 'road' | 'street' | 'dirt'; export type TrackDifficulty = 'beginner' | 'intermediate' | 'advanced' | 'expert'; export class Track implements IEntity { readonly id: string; readonly name: TrackName; readonly shortName: TrackShortName; readonly country: TrackCountry; readonly category: TrackCategory; readonly difficulty: TrackDifficulty; readonly lengthKm: TrackLength; readonly turns: TrackTurns; readonly imageUrl: TrackImageUrl; readonly gameId: TrackGameId; private constructor(props: { id: string; name: TrackName; shortName: TrackShortName; country: TrackCountry; category: TrackCategory; difficulty: TrackDifficulty; lengthKm: TrackLength; turns: TrackTurns; imageUrl: TrackImageUrl; gameId: TrackGameId; }) { this.id = props.id; this.name = props.name; this.shortName = props.shortName; this.country = props.country; this.category = props.category; this.difficulty = props.difficulty; this.lengthKm = props.lengthKm; this.turns = props.turns; this.imageUrl = props.imageUrl; this.gameId = props.gameId; } /** * Factory method to create a new Track entity */ static create(props: { id: string; name: string; shortName?: string; country: string; category?: TrackCategory; difficulty?: TrackDifficulty; lengthKm: number; turns: number; imageUrl?: string; gameId: string; }): Track { if (!props.id || props.id.trim().length === 0) { throw new RacingDomainValidationError('Track ID is required'); } const shortNameValue = props.shortName ?? props.name.slice(0, 3).toUpperCase(); return new Track({ id: props.id, name: TrackName.create(props.name), shortName: TrackShortName.create(shortNameValue), country: TrackCountry.create(props.country), category: props.category ?? 'road', difficulty: props.difficulty ?? 'intermediate', lengthKm: TrackLength.create(props.lengthKm), turns: TrackTurns.create(props.turns), imageUrl: TrackImageUrl.create(props.imageUrl), gameId: TrackGameId.create(props.gameId), }); } }