/** * Domain Entity: Result * * Represents a race result 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 { RaceId } from '../RaceId'; import { DriverId } from '../DriverId'; import { Position } from './Position'; import { LapTime } from './LapTime'; import { IncidentCount } from './IncidentCount'; export class Result implements IEntity { readonly id: string; readonly raceId: RaceId; readonly driverId: DriverId; readonly position: Position; readonly fastestLap: LapTime; readonly incidents: IncidentCount; readonly startPosition: Position; private constructor(props: { id: string; raceId: RaceId; driverId: DriverId; position: Position; fastestLap: LapTime; incidents: IncidentCount; startPosition: Position; }) { this.id = props.id; this.raceId = props.raceId; this.driverId = props.driverId; this.position = props.position; this.fastestLap = props.fastestLap; this.incidents = props.incidents; this.startPosition = props.startPosition; } /** * Factory method to create a new Result entity */ static create(props: { id: string; raceId: string; driverId: string; position: number; fastestLap: number; incidents: number; startPosition: number; }): Result { this.validate(props); const raceId = RaceId.create(props.raceId); const driverId = DriverId.create(props.driverId); const position = Position.create(props.position); const fastestLap = LapTime.create(props.fastestLap); const incidents = IncidentCount.create(props.incidents); const startPosition = Position.create(props.startPosition); return new Result({ id: props.id, raceId, driverId, position, fastestLap, incidents, startPosition, }); } /** * Domain validation logic */ private static validate(props: { id: string; raceId: string; driverId: string; position: number; fastestLap: number; incidents: number; startPosition: number; }): void { if (!props.id || props.id.trim().length === 0) { throw new RacingDomainValidationError('Result ID is required'); } if (!props.raceId || props.raceId.trim().length === 0) { throw new RacingDomainValidationError('Race ID is required'); } if (!props.driverId || props.driverId.trim().length === 0) { throw new RacingDomainValidationError('Driver ID is required'); } } /** * Calculate positions gained/lost */ getPositionChange(): number { return this.startPosition.toNumber() - this.position.toNumber(); } /** * Check if driver finished on podium */ isPodium(): boolean { return this.position.toNumber() <= 3; } /** * Check if driver had a clean race (0 incidents) */ isClean(): boolean { return this.incidents.toNumber() === 0; } }