168 lines
5.2 KiB
TypeScript
168 lines
5.2 KiB
TypeScript
/**
|
|
* Domain Entity: Team
|
|
*
|
|
* Represents a racing team in the GridPilot platform.
|
|
* Implements the shared Entity<string> contract and encapsulates
|
|
* basic invariants around identity and core properties.
|
|
*/
|
|
|
|
import { MediaReference } from '@core/domain/media/MediaReference';
|
|
import { Entity } from '@core/shared/domain/Entity';
|
|
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
|
import { TeamCreatedAt } from '../value-objects/TeamCreatedAt';
|
|
import { TeamDescription } from '../value-objects/TeamDescription';
|
|
import { TeamName } from '../value-objects/TeamName';
|
|
import { TeamTag } from '../value-objects/TeamTag';
|
|
import { DriverId } from './DriverId';
|
|
import { LeagueId } from './LeagueId';
|
|
|
|
export class Team extends Entity<string> {
|
|
readonly name: TeamName;
|
|
readonly tag: TeamTag;
|
|
readonly description: TeamDescription;
|
|
readonly ownerId: DriverId;
|
|
readonly leagues: LeagueId[];
|
|
readonly category: string | undefined;
|
|
readonly isRecruiting: boolean;
|
|
readonly createdAt: TeamCreatedAt;
|
|
readonly logoRef: MediaReference;
|
|
|
|
private constructor(props: {
|
|
id: string;
|
|
name: TeamName;
|
|
tag: TeamTag;
|
|
description: TeamDescription;
|
|
ownerId: DriverId;
|
|
leagues: LeagueId[];
|
|
category: string | undefined;
|
|
isRecruiting: boolean;
|
|
createdAt: TeamCreatedAt;
|
|
logoRef: MediaReference;
|
|
}) {
|
|
super(props.id);
|
|
|
|
this.name = props.name;
|
|
this.tag = props.tag;
|
|
this.description = props.description;
|
|
this.ownerId = props.ownerId;
|
|
this.leagues = props.leagues;
|
|
this.category = props.category;
|
|
this.isRecruiting = props.isRecruiting;
|
|
this.createdAt = props.createdAt;
|
|
this.logoRef = props.logoRef;
|
|
}
|
|
|
|
/**
|
|
* Factory method to create a new Team entity.
|
|
*/
|
|
static create(props: {
|
|
id: string;
|
|
name: string;
|
|
tag: string;
|
|
description: string;
|
|
ownerId: string;
|
|
leagues: string[];
|
|
category?: string;
|
|
isRecruiting?: boolean;
|
|
createdAt?: Date;
|
|
logoRef?: MediaReference;
|
|
}): Team {
|
|
if (!props.id || props.id.trim().length === 0) {
|
|
throw new RacingDomainValidationError('Team ID is required');
|
|
}
|
|
|
|
if (!Array.isArray(props.leagues)) {
|
|
throw new RacingDomainValidationError('Team leagues must be an array');
|
|
}
|
|
|
|
return new Team({
|
|
id: props.id,
|
|
name: TeamName.create(props.name),
|
|
tag: TeamTag.create(props.tag),
|
|
description: TeamDescription.create(props.description),
|
|
ownerId: DriverId.create(props.ownerId),
|
|
leagues: props.leagues.map(leagueId => LeagueId.create(leagueId)),
|
|
category: props.category,
|
|
isRecruiting: props.isRecruiting ?? false,
|
|
createdAt: TeamCreatedAt.create(props.createdAt ?? new Date()),
|
|
logoRef: props.logoRef ?? MediaReference.createSystemDefault('logo'),
|
|
});
|
|
}
|
|
|
|
static rehydrate(props: {
|
|
id: string;
|
|
name: string;
|
|
tag: string;
|
|
description: string;
|
|
ownerId: string;
|
|
leagues: string[];
|
|
category?: string;
|
|
isRecruiting: boolean;
|
|
createdAt: Date;
|
|
logoRef?: MediaReference;
|
|
}): Team {
|
|
if (!props.id || props.id.trim().length === 0) {
|
|
throw new RacingDomainValidationError('Team ID is required');
|
|
}
|
|
|
|
if (!Array.isArray(props.leagues)) {
|
|
throw new RacingDomainValidationError('Team leagues must be an array');
|
|
}
|
|
|
|
return new Team({
|
|
id: props.id,
|
|
name: TeamName.create(props.name),
|
|
tag: TeamTag.create(props.tag),
|
|
description: TeamDescription.create(props.description),
|
|
ownerId: DriverId.create(props.ownerId),
|
|
leagues: props.leagues.map(leagueId => LeagueId.create(leagueId)),
|
|
category: props.category,
|
|
isRecruiting: props.isRecruiting,
|
|
createdAt: TeamCreatedAt.create(props.createdAt),
|
|
logoRef: props.logoRef ?? MediaReference.createSystemDefault('logo'),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create a copy with updated properties.
|
|
*/
|
|
update(props: Partial<{
|
|
name: string;
|
|
tag: string;
|
|
description: string;
|
|
ownerId: string;
|
|
leagues: string[];
|
|
category: string | undefined;
|
|
isRecruiting: boolean;
|
|
logoRef: MediaReference;
|
|
}>): Team {
|
|
const nextName = 'name' in props ? TeamName.create(props.name!) : this.name;
|
|
const nextTag = 'tag' in props ? TeamTag.create(props.tag!) : this.tag;
|
|
const nextDescription = 'description' in props ? TeamDescription.create(props.description!) : this.description;
|
|
const nextOwnerId = 'ownerId' in props ? DriverId.create(props.ownerId!) : this.ownerId;
|
|
const nextLeagues = 'leagues' in props ? props.leagues!.map(leagueId => LeagueId.create(leagueId)) : this.leagues;
|
|
const nextCategory = 'category' in props ? props.category : this.category;
|
|
const nextIsRecruiting = 'isRecruiting' in props ? props.isRecruiting! : this.isRecruiting;
|
|
const nextLogoRef = 'logoRef' in props ? props.logoRef! : this.logoRef;
|
|
|
|
return new Team({
|
|
id: this.id,
|
|
name: nextName,
|
|
tag: nextTag,
|
|
description: nextDescription,
|
|
ownerId: nextOwnerId,
|
|
leagues: nextLeagues,
|
|
category: nextCategory,
|
|
isRecruiting: nextIsRecruiting,
|
|
createdAt: this.createdAt,
|
|
logoRef: nextLogoRef,
|
|
});
|
|
}
|
|
|
|
equals(other: Entity<string>): boolean {
|
|
if (!(other instanceof Team)) {
|
|
return false;
|
|
}
|
|
return this.id === other.id;
|
|
}
|
|
} |