/** * Domain Entity: Race * * Represents a race/session in the GridPilot platform. * Immutable entity with factory methods and domain validation. */ export type SessionType = 'practice' | 'qualifying' | 'race'; export type RaceStatus = 'scheduled' | 'completed' | 'cancelled'; export class Race { readonly id: string; readonly leagueId: string; readonly scheduledAt: Date; readonly track: string; readonly car: string; readonly sessionType: SessionType; readonly status: RaceStatus; private constructor(props: { id: string; leagueId: string; scheduledAt: Date; track: string; car: string; sessionType: SessionType; status: RaceStatus; }) { this.id = props.id; this.leagueId = props.leagueId; this.scheduledAt = props.scheduledAt; this.track = props.track; this.car = props.car; this.sessionType = props.sessionType; this.status = props.status; } /** * Factory method to create a new Race entity */ static create(props: { id: string; leagueId: string; scheduledAt: Date; track: string; car: string; sessionType?: SessionType; status?: RaceStatus; }): Race { this.validate(props); return new Race({ id: props.id, leagueId: props.leagueId, scheduledAt: props.scheduledAt, track: props.track, car: props.car, sessionType: props.sessionType ?? 'race', status: props.status ?? 'scheduled', }); } /** * Domain validation logic */ private static validate(props: { id: string; leagueId: string; scheduledAt: Date; track: string; car: string; }): void { if (!props.id || props.id.trim().length === 0) { throw new Error('Race ID is required'); } if (!props.leagueId || props.leagueId.trim().length === 0) { throw new Error('League ID is required'); } if (!props.scheduledAt || !(props.scheduledAt instanceof Date)) { throw new Error('Valid scheduled date is required'); } if (!props.track || props.track.trim().length === 0) { throw new Error('Track is required'); } if (!props.car || props.car.trim().length === 0) { throw new Error('Car is required'); } } /** * Mark race as completed */ complete(): Race { if (this.status === 'completed') { throw new Error('Race is already completed'); } if (this.status === 'cancelled') { throw new Error('Cannot complete a cancelled race'); } return new Race({ ...this, status: 'completed', }); } /** * Cancel the race */ cancel(): Race { if (this.status === 'completed') { throw new Error('Cannot cancel a completed race'); } if (this.status === 'cancelled') { throw new Error('Race is already cancelled'); } return new Race({ ...this, status: 'cancelled', }); } /** * Check if race is in the past */ isPast(): boolean { return this.scheduledAt < new Date(); } /** * Check if race is upcoming */ isUpcoming(): boolean { return this.status === 'scheduled' && !this.isPast(); } }