This commit is contained in:
2025-12-17 00:33:13 +01:00
parent 8c67081953
commit f01e01e50c
186 changed files with 9242 additions and 1342 deletions

View File

@@ -0,0 +1,86 @@
/**
* Domain Entity: Sponsor
*
* Represents a sponsor that can sponsor leagues/seasons.
* Aggregate root for sponsor information.
*/
import type { IEntity } from '@core/shared/domain';
import { SponsorId } from './SponsorId';
import { SponsorName } from './SponsorName';
import { SponsorEmail } from './SponsorEmail';
import { Url } from './Url';
import { SponsorCreatedAt } from './SponsorCreatedAt';
export class Sponsor implements IEntity<SponsorId> {
readonly id: SponsorId;
readonly name: SponsorName;
readonly contactEmail: SponsorEmail;
readonly logoUrl: Url | undefined;
readonly websiteUrl: Url | undefined;
readonly createdAt: SponsorCreatedAt;
private constructor(props: {
id: SponsorId;
name: SponsorName;
contactEmail: SponsorEmail;
logoUrl?: Url;
websiteUrl?: Url;
createdAt: SponsorCreatedAt;
}) {
this.id = props.id;
this.name = props.name;
this.contactEmail = props.contactEmail;
this.logoUrl = props.logoUrl;
this.websiteUrl = props.websiteUrl;
this.createdAt = props.createdAt;
}
static create(props: {
id: string;
name: string;
contactEmail: string;
logoUrl?: string;
websiteUrl?: string;
createdAt?: Date;
}): Sponsor {
const id = SponsorId.create(props.id);
const name = SponsorName.create(props.name);
const contactEmail = SponsorEmail.create(props.contactEmail);
const logoUrl = props.logoUrl ? Url.create(props.logoUrl) : undefined;
const websiteUrl = props.websiteUrl ? Url.create(props.websiteUrl) : undefined;
const createdAt = SponsorCreatedAt.create(props.createdAt ?? new Date());
return new Sponsor({
id,
name,
contactEmail,
createdAt,
...(logoUrl !== undefined ? { logoUrl } : {}),
...(websiteUrl !== undefined ? { websiteUrl } : {}),
});
}
/**
* Update sponsor information
*/
update(props: Partial<{
name: string;
contactEmail: string;
logoUrl?: string;
websiteUrl?: string;
}>): Sponsor {
const name = props.name ? SponsorName.create(props.name) : this.name;
const contactEmail = props.contactEmail ? SponsorEmail.create(props.contactEmail) : this.contactEmail;
const logoUrl = props.logoUrl !== undefined ? (props.logoUrl ? Url.create(props.logoUrl) : undefined) : this.logoUrl;
const websiteUrl = props.websiteUrl !== undefined ? (props.websiteUrl ? Url.create(props.websiteUrl) : undefined) : this.websiteUrl;
return new Sponsor({
id: this.id,
name,
contactEmail,
logoUrl,
websiteUrl,
createdAt: this.createdAt,
});
}
}

View File

@@ -0,0 +1,21 @@
import { RacingDomainValidationError } from '../../errors/RacingDomainError';
export class SponsorCreatedAt {
private constructor(private readonly value: Date) {}
static create(value: Date): SponsorCreatedAt {
const now = new Date();
if (value > now) {
throw new RacingDomainValidationError('Created date cannot be in the future');
}
return new SponsorCreatedAt(new Date(value));
}
toDate(): Date {
return new Date(this.value);
}
equals(other: SponsorCreatedAt): boolean {
return this.value.getTime() === other.value.getTime();
}
}

View File

@@ -0,0 +1,24 @@
import { RacingDomainValidationError } from '../../errors/RacingDomainError';
export class SponsorEmail {
private constructor(private readonly value: string) {}
static create(value: string): SponsorEmail {
if (!value || value.trim().length === 0) {
throw new RacingDomainValidationError('Sponsor contact email cannot be empty');
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
throw new RacingDomainValidationError('Invalid sponsor contact email format');
}
return new SponsorEmail(value.trim());
}
toString(): string {
return this.value;
}
equals(other: SponsorEmail): boolean {
return this.value === other.value;
}
}

View File

@@ -0,0 +1,20 @@
import { RacingDomainValidationError } from '../../errors/RacingDomainError';
export class SponsorId {
private constructor(private readonly value: string) {}
static create(value: string): SponsorId {
if (!value || value.trim().length === 0) {
throw new RacingDomainValidationError('Sponsor ID cannot be empty');
}
return new SponsorId(value.trim());
}
toString(): string {
return this.value;
}
equals(other: SponsorId): boolean {
return this.value === other.value;
}
}

View File

@@ -0,0 +1,23 @@
import { RacingDomainValidationError } from '../../errors/RacingDomainError';
export class SponsorName {
private constructor(private readonly value: string) {}
static create(value: string): SponsorName {
if (!value || value.trim().length === 0) {
throw new RacingDomainValidationError('Sponsor name cannot be empty');
}
if (value.length > 100) {
throw new RacingDomainValidationError('Sponsor name cannot exceed 100 characters');
}
return new SponsorName(value.trim());
}
toString(): string {
return this.value;
}
equals(other: SponsorName): boolean {
return this.value === other.value;
}
}

View File

@@ -0,0 +1,25 @@
import { RacingDomainValidationError } from '../../errors/RacingDomainError';
export class Url {
private constructor(private readonly value: string) {}
static create(value: string): Url {
if (!value || value.trim().length === 0) {
throw new RacingDomainValidationError('URL cannot be empty');
}
try {
new URL(value);
} catch {
throw new RacingDomainValidationError('Invalid URL format');
}
return new Url(value.trim());
}
toString(): string {
return this.value;
}
equals(other: Url): boolean {
return this.value === other.value;
}
}