/** * Domain Entity: League * * Represents a league in the GridPilot platform. * Immutable entity with factory methods and domain validation. */ export interface LeagueSettings { pointsSystem: 'f1-2024' | 'indycar' | 'custom'; sessionDuration?: number; qualifyingFormat?: 'single-lap' | 'open'; customPoints?: Record; /** * Maximum number of drivers allowed in the league. * Used for simple capacity display on the website. */ maxDrivers?: number; } export interface LeagueSocialLinks { discordUrl?: string; youtubeUrl?: string; websiteUrl?: string; } export class League { readonly id: string; readonly name: string; readonly description: string; readonly ownerId: string; readonly settings: LeagueSettings; readonly createdAt: Date; readonly socialLinks?: LeagueSocialLinks; private constructor(props: { id: string; name: string; description: string; ownerId: string; settings: LeagueSettings; createdAt: Date; socialLinks?: LeagueSocialLinks; }) { this.id = props.id; this.name = props.name; this.description = props.description; this.ownerId = props.ownerId; this.settings = props.settings; this.createdAt = props.createdAt; this.socialLinks = props.socialLinks; } /** * Factory method to create a new League entity */ static create(props: { id: string; name: string; description: string; ownerId: string; settings?: Partial; createdAt?: Date; socialLinks?: LeagueSocialLinks; }): League { this.validate(props); const defaultSettings: LeagueSettings = { pointsSystem: 'f1-2024', sessionDuration: 60, qualifyingFormat: 'open', maxDrivers: 32, }; return new League({ id: props.id, name: props.name, description: props.description, ownerId: props.ownerId, settings: { ...defaultSettings, ...props.settings }, createdAt: props.createdAt ?? new Date(), socialLinks: props.socialLinks, }); } /** * Domain validation logic */ private static validate(props: { id: string; name: string; description: string; ownerId: string; }): void { if (!props.id || props.id.trim().length === 0) { throw new Error('League ID is required'); } if (!props.name || props.name.trim().length === 0) { throw new Error('League name is required'); } if (props.name.length > 100) { throw new Error('League name must be 100 characters or less'); } if (!props.description || props.description.trim().length === 0) { throw new Error('League description is required'); } if (!props.ownerId || props.ownerId.trim().length === 0) { throw new Error('League owner ID is required'); } } /** * Create a copy with updated properties */ update(props: Partial<{ name: string; description: string; settings: LeagueSettings; socialLinks: LeagueSocialLinks | undefined; }>): League { return new League({ id: this.id, name: props.name ?? this.name, description: props.description ?? this.description, ownerId: this.ownerId, settings: props.settings ?? this.settings, createdAt: this.createdAt, socialLinks: props.socialLinks ?? this.socialLinks, }); } }