Files
gridpilot.gg/packages/racing/domain/entities/Car.ts
2025-12-12 01:11:36 +01:00

133 lines
3.5 KiB
TypeScript

/**
* Domain Entity: Car
*
* Represents a racing car/vehicle in the GridPilot platform.
* Immutable entity with factory methods and domain validation.
*/
import type { IEntity } from '@gridpilot/shared/domain';
import { RacingDomainValidationError } from '../errors/RacingDomainError';
export type CarClass = 'formula' | 'gt' | 'prototype' | 'touring' | 'sports' | 'oval' | 'dirt';
export type CarLicense = 'R' | 'D' | 'C' | 'B' | 'A' | 'Pro';
export class Car implements IEntity<string> {
readonly id: string;
readonly name: string;
readonly shortName: string;
readonly manufacturer: string;
readonly carClass: CarClass;
readonly license: CarLicense;
readonly year: number;
readonly horsepower: number | undefined;
readonly weight: number | undefined;
readonly imageUrl: string | undefined;
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(),
...(props.horsepower !== undefined ? { horsepower: props.horsepower } : {}),
...(props.weight !== undefined ? { weight: props.weight } : {}),
...(props.imageUrl !== undefined ? { 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 RacingDomainValidationError('Car ID is required');
}
if (!props.name || props.name.trim().length === 0) {
throw new RacingDomainValidationError('Car name is required');
}
if (!props.manufacturer || props.manufacturer.trim().length === 0) {
throw new RacingDomainValidationError('Car manufacturer is required');
}
if (!props.gameId || props.gameId.trim().length === 0) {
throw new RacingDomainValidationError('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];
}
}