/** * Domain Entity: Standing * * Represents a championship standing in the GridPilot platform. * Immutable entity with factory methods and domain validation. */ export class Standing { readonly leagueId: string; readonly driverId: string; readonly points: number; readonly wins: number; readonly position: number; readonly racesCompleted: number; private constructor(props: { leagueId: string; driverId: string; points: number; wins: number; position: number; racesCompleted: number; }) { this.leagueId = props.leagueId; this.driverId = props.driverId; this.points = props.points; this.wins = props.wins; this.position = props.position; this.racesCompleted = props.racesCompleted; } /** * Factory method to create a new Standing entity */ static create(props: { leagueId: string; driverId: string; points?: number; wins?: number; position?: number; racesCompleted?: number; }): Standing { this.validate(props); return new Standing({ leagueId: props.leagueId, driverId: props.driverId, points: props.points ?? 0, wins: props.wins ?? 0, position: props.position ?? 0, racesCompleted: props.racesCompleted ?? 0, }); } /** * Domain validation logic */ private static validate(props: { leagueId: string; driverId: string; }): void { if (!props.leagueId || props.leagueId.trim().length === 0) { throw new Error('League ID is required'); } if (!props.driverId || props.driverId.trim().length === 0) { throw new Error('Driver ID is required'); } } /** * Add points from a race result */ addRaceResult(position: number, pointsSystem: Record): Standing { const racePoints = pointsSystem[position] ?? 0; const isWin = position === 1; return new Standing({ leagueId: this.leagueId, driverId: this.driverId, points: this.points + racePoints, wins: this.wins + (isWin ? 1 : 0), position: this.position, racesCompleted: this.racesCompleted + 1, }); } /** * Update championship position */ updatePosition(position: number): Standing { if (!Number.isInteger(position) || position < 1) { throw new Error('Position must be a positive integer'); } return new Standing({ ...this, position, }); } /** * Calculate average points per race */ getAveragePoints(): number { if (this.racesCompleted === 0) return 0; return this.points / this.racesCompleted; } /** * Calculate win percentage */ getWinPercentage(): number { if (this.racesCompleted === 0) return 0; return (this.wins / this.racesCompleted) * 100; } }