Files
gridpilot.gg/packages/racing/domain/entities/SeasonSponsorship.ts
2025-12-11 11:25:22 +01:00

142 lines
3.7 KiB
TypeScript

/**
* Domain Entity: SeasonSponsorship
*
* Represents a sponsorship relationship between a Sponsor and a Season.
* Aggregate root for managing sponsorship slots and pricing.
*/
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
import type { Money } from '../value-objects/Money';
export type SponsorshipTier = 'main' | 'secondary';
export type SponsorshipStatus = 'pending' | 'active' | 'cancelled';
export interface SeasonSponsorshipProps {
id: string;
seasonId: string;
sponsorId: string;
tier: SponsorshipTier;
pricing: Money;
status: SponsorshipStatus;
createdAt: Date;
activatedAt?: Date;
description?: string;
}
export class SeasonSponsorship {
readonly id: string;
readonly seasonId: string;
readonly sponsorId: string;
readonly tier: SponsorshipTier;
readonly pricing: Money;
readonly status: SponsorshipStatus;
readonly createdAt: Date;
readonly activatedAt?: Date;
readonly description?: string;
private constructor(props: SeasonSponsorshipProps) {
this.id = props.id;
this.seasonId = props.seasonId;
this.sponsorId = props.sponsorId;
this.tier = props.tier;
this.pricing = props.pricing;
this.status = props.status;
this.createdAt = props.createdAt;
this.activatedAt = props.activatedAt;
this.description = props.description;
}
static create(props: Omit<SeasonSponsorshipProps, 'createdAt' | 'status'> & {
createdAt?: Date;
status?: SponsorshipStatus;
}): SeasonSponsorship {
this.validate(props);
return new SeasonSponsorship({
...props,
createdAt: props.createdAt ?? new Date(),
status: props.status ?? 'pending',
});
}
private static validate(props: Omit<SeasonSponsorshipProps, 'createdAt' | 'status'>): void {
if (!props.id || props.id.trim().length === 0) {
throw new RacingDomainValidationError('SeasonSponsorship ID is required');
}
if (!props.seasonId || props.seasonId.trim().length === 0) {
throw new RacingDomainValidationError('SeasonSponsorship seasonId is required');
}
if (!props.sponsorId || props.sponsorId.trim().length === 0) {
throw new RacingDomainValidationError('SeasonSponsorship sponsorId is required');
}
if (!props.tier) {
throw new RacingDomainValidationError('SeasonSponsorship tier is required');
}
if (!props.pricing) {
throw new RacingDomainValidationError('SeasonSponsorship pricing is required');
}
if (props.pricing.amount <= 0) {
throw new RacingDomainValidationError('SeasonSponsorship pricing must be greater than zero');
}
}
/**
* Activate the sponsorship
*/
activate(): SeasonSponsorship {
if (this.status === 'active') {
throw new RacingDomainInvariantError('SeasonSponsorship is already active');
}
if (this.status === 'cancelled') {
throw new RacingDomainInvariantError('Cannot activate a cancelled SeasonSponsorship');
}
return new SeasonSponsorship({
...this,
status: 'active',
activatedAt: new Date(),
});
}
/**
* Cancel the sponsorship
*/
cancel(): SeasonSponsorship {
if (this.status === 'cancelled') {
throw new RacingDomainInvariantError('SeasonSponsorship is already cancelled');
}
return new SeasonSponsorship({
...this,
status: 'cancelled',
});
}
/**
* Check if sponsorship is active
*/
isActive(): boolean {
return this.status === 'active';
}
/**
* Get the platform fee for this sponsorship
*/
getPlatformFee(): Money {
return this.pricing.calculatePlatformFee();
}
/**
* Get the net amount after platform fee
*/
getNetAmount(): Money {
return this.pricing.calculateNetAmount();
}
}