Files
gridpilot.gg/core/racing/domain/entities/Track.ts
2026-01-16 16:46:57 +01:00

91 lines
2.8 KiB
TypeScript

/**
* 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<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;
}) {
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),
});
}
}