/** * Domain Entity: Driver * * Represents a driver profile in the GridPilot platform. * Immutable entity with factory methods and domain validation. */ import { RacingDomainValidationError } from '../errors/RacingDomainError'; import type { IEntity } from '@gridpilot/shared/domain'; export class Driver implements IEntity { readonly id: string; readonly iracingId: string; readonly name: string; readonly country: string; readonly bio: string | undefined; 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({ id: props.id, iracingId: props.iracingId, name: props.name, country: props.country, ...(props.bio !== undefined ? { bio: props.bio } : {}), 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 RacingDomainValidationError('Driver ID is required'); } if (!props.iracingId || props.iracingId.trim().length === 0) { throw new RacingDomainValidationError('iRacing ID is required'); } if (!props.name || props.name.trim().length === 0) { throw new RacingDomainValidationError('Driver name is required'); } if (!props.country || props.country.trim().length === 0) { throw new RacingDomainValidationError('Country code is required'); } // Validate ISO country code format (2-3 letters) if (!/^[A-Z]{2,3}$/i.test(props.country)) { throw new RacingDomainValidationError('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 { const nextName = props.name ?? this.name; const nextCountry = props.country ?? this.country; const nextBio = props.bio ?? this.bio; return new Driver({ id: this.id, iracingId: this.iracingId, name: nextName, country: nextCountry, ...(nextBio !== undefined ? { bio: nextBio } : {}), joinedAt: this.joinedAt, }); } }