/** * Domain Entity: Track * * Represents a racing track/circuit in the GridPilot platform. * Immutable entity with factory methods and domain validation. */ import { Entity } from '@core/shared/domain/Entity'; import { RacingDomainValidationError } from '../errors/RacingDomainError'; import { TrackCountry } from '../value-objects/TrackCountry'; import { TrackGameId } from '../value-objects/TrackGameId'; import { TrackImageUrl } from '../value-objects/TrackImageUrl'; import { TrackLength } from '../value-objects/TrackLength'; import { TrackName } from '../value-objects/TrackName'; import { TrackShortName } from '../value-objects/TrackShortName'; import { TrackTurns } from '../value-objects/TrackTurns'; export type TrackCategory = 'oval' | 'road' | 'street' | 'dirt'; export type TrackDifficulty = 'beginner' | 'intermediate' | 'advanced' | 'expert'; export class Track extends Entity { 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; }) { super(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), }); } update(props: Partial<{ name: string; shortName: string; country: string; category: TrackCategory; difficulty: TrackDifficulty; lengthKm: number; turns: number; imageUrl: string; gameId: string; }>): Track { return new Track({ id: this.id, name: props.name ? TrackName.create(props.name) : this.name, shortName: props.shortName ? TrackShortName.create(props.shortName) : this.shortName, country: props.country ? TrackCountry.create(props.country) : this.country, category: props.category ?? this.category, difficulty: props.difficulty ?? this.difficulty, lengthKm: props.lengthKm ? TrackLength.create(props.lengthKm) : this.lengthKm, turns: props.turns ? TrackTurns.create(props.turns) : this.turns, imageUrl: props.imageUrl ? TrackImageUrl.create(props.imageUrl) : this.imageUrl, gameId: props.gameId ? TrackGameId.create(props.gameId) : this.gameId, }); } }