130 lines
3.1 KiB
TypeScript
130 lines
3.1 KiB
TypeScript
/**
|
|
* Domain Entity: Car
|
|
*
|
|
* Represents a racing car/vehicle in the GridPilot platform.
|
|
* Immutable entity with factory methods and domain validation.
|
|
*/
|
|
|
|
export type CarClass = 'formula' | 'gt' | 'prototype' | 'touring' | 'sports' | 'oval' | 'dirt';
|
|
export type CarLicense = 'R' | 'D' | 'C' | 'B' | 'A' | 'Pro';
|
|
|
|
export class Car {
|
|
readonly id: string;
|
|
readonly name: string;
|
|
readonly shortName: string;
|
|
readonly manufacturer: string;
|
|
readonly carClass: CarClass;
|
|
readonly license: CarLicense;
|
|
readonly year: number;
|
|
readonly horsepower?: number;
|
|
readonly weight?: number;
|
|
readonly imageUrl?: string;
|
|
readonly gameId: string;
|
|
|
|
private constructor(props: {
|
|
id: string;
|
|
name: string;
|
|
shortName: string;
|
|
manufacturer: string;
|
|
carClass: CarClass;
|
|
license: CarLicense;
|
|
year: number;
|
|
horsepower?: number;
|
|
weight?: number;
|
|
imageUrl?: string;
|
|
gameId: string;
|
|
}) {
|
|
this.id = props.id;
|
|
this.name = props.name;
|
|
this.shortName = props.shortName;
|
|
this.manufacturer = props.manufacturer;
|
|
this.carClass = props.carClass;
|
|
this.license = props.license;
|
|
this.year = props.year;
|
|
this.horsepower = props.horsepower;
|
|
this.weight = props.weight;
|
|
this.imageUrl = props.imageUrl;
|
|
this.gameId = props.gameId;
|
|
}
|
|
|
|
/**
|
|
* Factory method to create a new Car entity
|
|
*/
|
|
static create(props: {
|
|
id: string;
|
|
name: string;
|
|
shortName?: string;
|
|
manufacturer: string;
|
|
carClass?: CarClass;
|
|
license?: CarLicense;
|
|
year?: number;
|
|
horsepower?: number;
|
|
weight?: number;
|
|
imageUrl?: string;
|
|
gameId: string;
|
|
}): Car {
|
|
this.validate(props);
|
|
|
|
return new Car({
|
|
id: props.id,
|
|
name: props.name,
|
|
shortName: props.shortName ?? props.name.slice(0, 10),
|
|
manufacturer: props.manufacturer,
|
|
carClass: props.carClass ?? 'gt',
|
|
license: props.license ?? 'D',
|
|
year: props.year ?? new Date().getFullYear(),
|
|
horsepower: props.horsepower,
|
|
weight: props.weight,
|
|
imageUrl: props.imageUrl,
|
|
gameId: props.gameId,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Domain validation logic
|
|
*/
|
|
private static validate(props: {
|
|
id: string;
|
|
name: string;
|
|
manufacturer: string;
|
|
gameId: string;
|
|
}): void {
|
|
if (!props.id || props.id.trim().length === 0) {
|
|
throw new Error('Car ID is required');
|
|
}
|
|
|
|
if (!props.name || props.name.trim().length === 0) {
|
|
throw new Error('Car name is required');
|
|
}
|
|
|
|
if (!props.manufacturer || props.manufacturer.trim().length === 0) {
|
|
throw new Error('Car manufacturer is required');
|
|
}
|
|
|
|
if (!props.gameId || props.gameId.trim().length === 0) {
|
|
throw new Error('Game ID is required');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get formatted car display name
|
|
*/
|
|
getDisplayName(): string {
|
|
return `${this.manufacturer} ${this.name}`;
|
|
}
|
|
|
|
/**
|
|
* Get license badge color
|
|
*/
|
|
getLicenseColor(): string {
|
|
const colors: Record<CarLicense, string> = {
|
|
'R': '#FF6B6B',
|
|
'D': '#FFB347',
|
|
'C': '#FFD700',
|
|
'B': '#7FFF00',
|
|
'A': '#00BFFF',
|
|
'Pro': '#9370DB',
|
|
};
|
|
return colors[this.license];
|
|
}
|
|
} |