Files
gridpilot.gg/apps/website/domain/entities/Driver.ts
2025-12-03 00:46:08 +01:00

99 lines
2.2 KiB
TypeScript

/**
* Domain Entity: Driver
*
* Represents a driver profile in the GridPilot platform.
* Immutable entity with factory methods and domain validation.
*/
export class Driver {
readonly id: string;
readonly iracingId: string;
readonly name: string;
readonly country: string;
readonly bio?: string;
readonly joinedAt: Date;
private constructor(props: {
id: string;
iracingId: string;
name: string;
country: string;
bio?: string;
joinedAt: Date;
}) {
this.id = props.id;
this.iracingId = props.iracingId;
this.name = props.name;
this.country = props.country;
this.bio = props.bio;
this.joinedAt = props.joinedAt;
}
/**
* Factory method to create a new Driver entity
*/
static create(props: {
id: string;
iracingId: string;
name: string;
country: string;
bio?: string;
joinedAt?: Date;
}): Driver {
this.validate(props);
return new Driver({
...props,
joinedAt: props.joinedAt ?? new Date(),
});
}
/**
* Domain validation logic
*/
private static validate(props: {
id: string;
iracingId: string;
name: string;
country: string;
}): void {
if (!props.id || props.id.trim().length === 0) {
throw new Error('Driver ID is required');
}
if (!props.iracingId || props.iracingId.trim().length === 0) {
throw new Error('iRacing ID is required');
}
if (!props.name || props.name.trim().length === 0) {
throw new Error('Driver name is required');
}
if (!props.country || props.country.trim().length === 0) {
throw new Error('Country code is required');
}
// Validate ISO country code format (2-3 letters)
if (!/^[A-Z]{2,3}$/i.test(props.country)) {
throw new Error('Country must be a valid ISO code (2-3 letters)');
}
}
/**
* Create a copy with updated properties
*/
update(props: Partial<{
name: string;
country: string;
bio: string;
}>): Driver {
return new Driver({
id: this.id,
iracingId: this.iracingId,
name: props.name ?? this.name,
country: props.country ?? this.country,
bio: props.bio ?? this.bio,
joinedAt: this.joinedAt,
});
}
}