/** * Carrier Entity * * Represents a shipping carrier (e.g., Maersk, MSC, CMA CGM) * * Business Rules: * - Carrier code must be unique * - SCAC code must be valid (4 uppercase letters) * - API configuration is optional (for carriers with API integration) */ export interface CarrierApiConfig { baseUrl: string; apiKey?: string; clientId?: string; clientSecret?: string; timeout: number; // in milliseconds retryAttempts: number; circuitBreakerThreshold: number; } export interface CarrierProps { id: string; name: string; code: string; // Unique carrier code (e.g., 'MAERSK', 'MSC') scac: string; // Standard Carrier Alpha Code logoUrl?: string; website?: string; apiConfig?: CarrierApiConfig; isActive: boolean; supportsApi: boolean; // True if carrier has API integration createdAt: Date; updatedAt: Date; } export class Carrier { private readonly props: CarrierProps; private constructor(props: CarrierProps) { this.props = props; } /** * Factory method to create a new Carrier */ static create(props: Omit): Carrier { const now = new Date(); // Validate SCAC code if (!Carrier.isValidSCAC(props.scac)) { throw new Error('Invalid SCAC code format. Must be 4 uppercase letters.'); } // Validate carrier code if (!Carrier.isValidCarrierCode(props.code)) { throw new Error( 'Invalid carrier code format. Must be uppercase letters and underscores only.' ); } // Validate API config if carrier supports API if (props.supportsApi && !props.apiConfig) { throw new Error('Carriers with API support must have API configuration.'); } return new Carrier({ ...props, createdAt: now, updatedAt: now, }); } /** * Factory method to reconstitute from persistence */ static fromPersistence(props: CarrierProps): Carrier { return new Carrier(props); } /** * Validate SCAC code format */ private static isValidSCAC(scac: string): boolean { const scacPattern = /^[A-Z]{4}$/; return scacPattern.test(scac); } /** * Validate carrier code format */ private static isValidCarrierCode(code: string): boolean { const codePattern = /^[A-Z_]+$/; return codePattern.test(code); } // Getters get id(): string { return this.props.id; } get name(): string { return this.props.name; } get code(): string { return this.props.code; } get scac(): string { return this.props.scac; } get logoUrl(): string | undefined { return this.props.logoUrl; } get website(): string | undefined { return this.props.website; } get apiConfig(): CarrierApiConfig | undefined { return this.props.apiConfig ? { ...this.props.apiConfig } : undefined; } get isActive(): boolean { return this.props.isActive; } get supportsApi(): boolean { return this.props.supportsApi; } get createdAt(): Date { return this.props.createdAt; } get updatedAt(): Date { return this.props.updatedAt; } // Business methods hasApiIntegration(): boolean { return this.props.supportsApi && !!this.props.apiConfig; } updateApiConfig(apiConfig: CarrierApiConfig): void { if (!this.props.supportsApi) { throw new Error('Cannot update API config for carrier without API support.'); } this.props.apiConfig = { ...apiConfig }; this.props.updatedAt = new Date(); } updateLogoUrl(logoUrl: string): void { this.props.logoUrl = logoUrl; this.props.updatedAt = new Date(); } updateWebsite(website: string): void { this.props.website = website; this.props.updatedAt = new Date(); } deactivate(): void { this.props.isActive = false; this.props.updatedAt = new Date(); } activate(): void { this.props.isActive = true; this.props.updatedAt = new Date(); } /** * Convert to plain object for persistence */ toObject(): CarrierProps { return { ...this.props, apiConfig: this.props.apiConfig ? { ...this.props.apiConfig } : undefined, }; } }