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

119 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 '@core/shared/domain';
import { RacingDomainValidationError } from '../errors/RacingDomainError';
import { CarName } from './CarName';
import { Manufacturer } from './Manufacturer';
import { CarClass, CarClassType } from './CarClass';
import { CarLicense, CarLicenseType } from './CarLicense';
import { Year } from './Year';
import { Horsepower } from './Horsepower';
import { Weight } from './Weight';
import { GameId } from './GameId';
import { CarId } from './CarId';
import { ImageUrl } from './ImageUrl';
export class Car implements IEntity<CarId> {
readonly id: CarId;
readonly name: CarName;
readonly shortName: string;
readonly manufacturer: Manufacturer;
readonly carClass: CarClass;
readonly license: CarLicense;
readonly year: Year;
readonly horsepower: Horsepower | undefined;
readonly weight: Weight | undefined;
readonly imageUrl: ImageUrl | undefined;
readonly gameId: GameId;
private constructor(props: {
id: CarId;
name: CarName;
shortName: string;
manufacturer: Manufacturer;
carClass: CarClass;
license: CarLicense;
year: Year;
horsepower?: Horsepower;
weight?: Weight;
imageUrl?: ImageUrl;
gameId: GameId;
}) {
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?: CarClassType;
license?: CarLicenseType;
year?: number;
horsepower?: number;
weight?: number;
imageUrl?: string;
gameId: string;
}): Car {
this.validate(props);
return new Car({
id: CarId.create(props.id),
name: CarName.create(props.name),
shortName: props.shortName ?? props.name.slice(0, 10),
manufacturer: Manufacturer.create(props.manufacturer),
carClass: CarClass.create(props.carClass ?? 'gt'),
license: CarLicense.create(props.license ?? 'D'),
year: Year.create(props.year ?? new Date().getFullYear()),
...(props.horsepower !== undefined ? { horsepower: Horsepower.create(props.horsepower) } : {}),
...(props.weight !== undefined ? { weight: Weight.create(props.weight) } : {}),
...(props.imageUrl !== undefined ? { imageUrl: ImageUrl.create(props.imageUrl) } : {}),
gameId: GameId.create(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');
}
}
}