Files
gridpilot.gg/core/racing/domain/entities/result/Result.ts
2025-12-17 00:33:13 +01:00

120 lines
3.0 KiB
TypeScript

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