alpha wip
This commit is contained in:
113
apps/website/domain/entities/Result.ts
Normal file
113
apps/website/domain/entities/Result.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Domain Entity: Result
|
||||
*
|
||||
* Represents a race result in the GridPilot platform.
|
||||
* Immutable entity with factory methods and domain validation.
|
||||
*/
|
||||
|
||||
export class Result {
|
||||
readonly id: string;
|
||||
readonly raceId: string;
|
||||
readonly driverId: string;
|
||||
readonly position: number;
|
||||
readonly fastestLap: number;
|
||||
readonly incidents: number;
|
||||
readonly startPosition: number;
|
||||
|
||||
private constructor(props: {
|
||||
id: string;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
position: number;
|
||||
fastestLap: number;
|
||||
incidents: number;
|
||||
startPosition: number;
|
||||
}) {
|
||||
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);
|
||||
|
||||
return new Result(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Error('Result ID is required');
|
||||
}
|
||||
|
||||
if (!props.raceId || props.raceId.trim().length === 0) {
|
||||
throw new Error('Race ID is required');
|
||||
}
|
||||
|
||||
if (!props.driverId || props.driverId.trim().length === 0) {
|
||||
throw new Error('Driver ID is required');
|
||||
}
|
||||
|
||||
if (!Number.isInteger(props.position) || props.position < 1) {
|
||||
throw new Error('Position must be a positive integer');
|
||||
}
|
||||
|
||||
if (props.fastestLap < 0) {
|
||||
throw new Error('Fastest lap cannot be negative');
|
||||
}
|
||||
|
||||
if (!Number.isInteger(props.incidents) || props.incidents < 0) {
|
||||
throw new Error('Incidents must be a non-negative integer');
|
||||
}
|
||||
|
||||
if (!Number.isInteger(props.startPosition) || props.startPosition < 1) {
|
||||
throw new Error('Start position must be a positive integer');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate positions gained/lost
|
||||
*/
|
||||
getPositionChange(): number {
|
||||
return this.startPosition - this.position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if driver finished on podium
|
||||
*/
|
||||
isPodium(): boolean {
|
||||
return this.position <= 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if driver had a clean race (0 incidents)
|
||||
*/
|
||||
isClean(): boolean {
|
||||
return this.incidents === 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user