wip
This commit is contained in:
@@ -1,17 +1,17 @@
|
||||
/**
|
||||
* Domain Entity: Car
|
||||
*/
|
||||
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
*
|
||||
*
|
||||
* Represents a racing car/vehicle in the GridPilot platform.
|
||||
* Immutable entity with factory methods and domain validation.
|
||||
*/
|
||||
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
|
||||
export type CarClass = 'formula' | 'gt' | 'prototype' | 'touring' | 'sports' | 'oval' | 'dirt';
|
||||
export type CarLicense = 'R' | 'D' | 'C' | 'B' | 'A' | 'Pro';
|
||||
|
||||
export class Car {
|
||||
export class Car implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly shortName: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ParticipantRef } from '../value-objects/ParticipantRef';
|
||||
import type { ParticipantRef } from '../types/ParticipantRef';
|
||||
|
||||
export class ChampionshipStanding {
|
||||
readonly seasonId: string;
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
* Represents a driver profile in the GridPilot platform.
|
||||
* Immutable entity with factory methods and domain validation.
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
|
||||
export class Driver {
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
export class Driver implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly iracingId: string;
|
||||
readonly name: string;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* Domain Entity: DriverLivery
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
*
|
||||
* Represents a driver's custom livery for a specific car.
|
||||
* Includes user-placed decals and league-specific overrides.
|
||||
@@ -31,7 +32,7 @@ export interface DriverLiveryProps {
|
||||
validatedAt?: Date;
|
||||
}
|
||||
|
||||
export class DriverLivery {
|
||||
export class DriverLivery implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly driverId: string;
|
||||
readonly gameId: string;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
|
||||
export class Game {
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
export class Game implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* Represents a league in the GridPilot platform.
|
||||
* Immutable entity with factory methods and domain validation.
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
/**
|
||||
* Stewarding decision mode for protests
|
||||
@@ -78,8 +79,8 @@ export interface LeagueSocialLinks {
|
||||
youtubeUrl?: string;
|
||||
websiteUrl?: string;
|
||||
}
|
||||
|
||||
export class League {
|
||||
|
||||
export class League implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
|
||||
@@ -1,19 +1,75 @@
|
||||
/**
|
||||
* Domain Entity: LeagueMembership and JoinRequest
|
||||
*
|
||||
* Extracted from racing-application memberships module so that
|
||||
* membership-related types live in the racing-domain package.
|
||||
* Represents a driver's membership in a league and join requests.
|
||||
*/
|
||||
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
|
||||
export type MembershipRole = 'owner' | 'admin' | 'steward' | 'member';
|
||||
export type MembershipStatus = 'active' | 'pending' | 'none';
|
||||
|
||||
export interface LeagueMembership {
|
||||
export interface LeagueMembershipProps {
|
||||
id?: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
role: MembershipRole;
|
||||
status: MembershipStatus;
|
||||
joinedAt: Date;
|
||||
status?: MembershipStatus;
|
||||
joinedAt?: Date;
|
||||
}
|
||||
|
||||
export class LeagueMembership implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly leagueId: string;
|
||||
readonly driverId: string;
|
||||
readonly role: MembershipRole;
|
||||
readonly status: MembershipStatus;
|
||||
readonly joinedAt: Date;
|
||||
|
||||
private constructor(props: Required<LeagueMembershipProps>) {
|
||||
this.id = props.id;
|
||||
this.leagueId = props.leagueId;
|
||||
this.driverId = props.driverId;
|
||||
this.role = props.role;
|
||||
this.status = props.status;
|
||||
this.joinedAt = props.joinedAt;
|
||||
}
|
||||
|
||||
static create(props: LeagueMembershipProps): LeagueMembership {
|
||||
this.validate(props);
|
||||
|
||||
const id =
|
||||
props.id && props.id.trim().length > 0
|
||||
? props.id
|
||||
: `${props.leagueId}:${props.driverId}`;
|
||||
|
||||
const status = props.status ?? 'pending';
|
||||
const joinedAt = props.joinedAt ?? new Date();
|
||||
|
||||
return new LeagueMembership({
|
||||
id,
|
||||
leagueId: props.leagueId,
|
||||
driverId: props.driverId,
|
||||
role: props.role,
|
||||
status,
|
||||
joinedAt,
|
||||
});
|
||||
}
|
||||
|
||||
private static validate(props: LeagueMembershipProps): void {
|
||||
if (!props.leagueId || props.leagueId.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('League ID is required');
|
||||
}
|
||||
|
||||
if (!props.driverId || props.driverId.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Driver ID is required');
|
||||
}
|
||||
|
||||
if (!props.role) {
|
||||
throw new RacingDomainValidationError('Membership role is required');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface JoinRequest {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ChampionshipConfig } from '../value-objects/ChampionshipConfig';
|
||||
import type { ChampionshipConfig } from '../types/ChampionshipConfig';
|
||||
|
||||
export interface LeagueScoringConfig {
|
||||
id: string;
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* Represents a league's financial wallet.
|
||||
* Aggregate root for managing league finances and transactions.
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
import type { Money } from '../value-objects/Money';
|
||||
import type { Transaction } from './Transaction';
|
||||
@@ -18,7 +19,7 @@ export interface LeagueWalletProps {
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export class LeagueWallet {
|
||||
export class LeagueWallet implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly leagueId: string;
|
||||
readonly balance: Money;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* Domain Entity: LiveryTemplate
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
*
|
||||
* Represents an admin-defined livery template for a specific car.
|
||||
* Contains base image and sponsor decal placements.
|
||||
@@ -21,7 +22,7 @@ export interface LiveryTemplateProps {
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export class LiveryTemplate {
|
||||
export class LiveryTemplate implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly leagueId: string;
|
||||
readonly seasonId: string;
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* Represents a penalty applied to a driver for an incident during a race.
|
||||
* Penalties can be applied as a result of an upheld protest or directly by stewards.
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
export type PenaltyType =
|
||||
| 'time_penalty' // Add time to race result (e.g., +5 seconds)
|
||||
@@ -45,7 +46,7 @@ export interface PenaltyProps {
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export class Penalty {
|
||||
export class Penalty implements IEntity<string> {
|
||||
private constructor(private readonly props: PenaltyProps) {}
|
||||
|
||||
static create(props: PenaltyProps): Penalty {
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
*
|
||||
* Represents a prize awarded to a driver for a specific position in a season.
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
import type { Money } from '../value-objects/Money';
|
||||
|
||||
@@ -23,7 +24,7 @@ export interface PrizeProps {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export class Prize {
|
||||
export class Prize implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly seasonId: string;
|
||||
readonly position: number;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* Domain Entity: Protest
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
*
|
||||
* Represents a protest filed by a driver against another driver for an incident during a race.
|
||||
*
|
||||
@@ -66,7 +67,7 @@ export interface ProtestProps {
|
||||
defenseRequestedBy?: string;
|
||||
}
|
||||
|
||||
export class Protest {
|
||||
export class Protest implements IEntity<string> {
|
||||
private constructor(private readonly props: ProtestProps) {}
|
||||
|
||||
static create(props: ProtestProps): Protest {
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
* Represents a race/session in the GridPilot platform.
|
||||
* Immutable entity with factory methods and domain validation.
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
export type SessionType = 'practice' | 'qualifying' | 'race';
|
||||
export type RaceStatus = 'scheduled' | 'running' | 'completed' | 'cancelled';
|
||||
|
||||
export class Race {
|
||||
|
||||
export class Race implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly leagueId: string;
|
||||
readonly scheduledAt: Date;
|
||||
|
||||
@@ -1,12 +1,57 @@
|
||||
/**
|
||||
* Domain Entity: RaceRegistration
|
||||
*
|
||||
* Extracted from racing-application registrations module so that
|
||||
* registration-related types live in the racing-domain package.
|
||||
* Represents a registration of a driver for a specific race.
|
||||
*/
|
||||
|
||||
export interface RaceRegistration {
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
|
||||
export interface RaceRegistrationProps {
|
||||
id?: string;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
registeredAt: Date;
|
||||
registeredAt?: Date;
|
||||
}
|
||||
|
||||
export class RaceRegistration implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly raceId: string;
|
||||
readonly driverId: string;
|
||||
readonly registeredAt: Date;
|
||||
|
||||
private constructor(props: Required<RaceRegistrationProps>) {
|
||||
this.id = props.id;
|
||||
this.raceId = props.raceId;
|
||||
this.driverId = props.driverId;
|
||||
this.registeredAt = props.registeredAt;
|
||||
}
|
||||
|
||||
static create(props: RaceRegistrationProps): RaceRegistration {
|
||||
this.validate(props);
|
||||
|
||||
const id =
|
||||
props.id && props.id.trim().length > 0
|
||||
? props.id
|
||||
: `${props.raceId}:${props.driverId}`;
|
||||
|
||||
const registeredAt = props.registeredAt ?? new Date();
|
||||
|
||||
return new RaceRegistration({
|
||||
id,
|
||||
raceId: props.raceId,
|
||||
driverId: props.driverId,
|
||||
registeredAt,
|
||||
});
|
||||
}
|
||||
|
||||
private static validate(props: RaceRegistrationProps): void {
|
||||
if (!props.raceId || props.raceId.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Race ID is required');
|
||||
}
|
||||
|
||||
if (!props.driverId || props.driverId.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Driver ID is required');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,11 @@
|
||||
* Represents a race result in the GridPilot platform.
|
||||
* Immutable entity with factory methods and domain validation.
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
export class Result {
|
||||
export class Result implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly raceId: string;
|
||||
readonly driverId: string;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
export type SeasonStatus = 'planned' | 'active' | 'completed';
|
||||
|
||||
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
|
||||
export class Season {
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
export class Season implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly leagueId: string;
|
||||
readonly gameId: string;
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* 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 { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
import type { Money } from '../value-objects/Money';
|
||||
|
||||
@@ -24,7 +25,7 @@ export interface SeasonSponsorshipProps {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export class SeasonSponsorship {
|
||||
export class SeasonSponsorship implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly seasonId: string;
|
||||
readonly sponsorId: string;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* Domain Entity: Sponsor
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
*
|
||||
* Represents a sponsor that can sponsor leagues/seasons.
|
||||
* Aggregate root for sponsor information.
|
||||
@@ -17,7 +18,7 @@ export interface SponsorProps {
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export class Sponsor {
|
||||
export class Sponsor implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly contactEmail: string;
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* Represents a sponsorship application from a Sponsor to any sponsorable entity
|
||||
* (driver, team, race, or league/season). The entity owner must approve/reject.
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
import type { Money } from '../value-objects/Money';
|
||||
import type { SponsorshipTier } from './SeasonSponsorship';
|
||||
@@ -28,7 +29,7 @@ export interface SponsorshipRequestProps {
|
||||
rejectionReason?: string;
|
||||
}
|
||||
|
||||
export class SponsorshipRequest {
|
||||
export class SponsorshipRequest implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly sponsorId: string;
|
||||
readonly entityType: SponsorableEntityType;
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
/**
|
||||
* Domain Entity: Standing
|
||||
*
|
||||
*
|
||||
* Represents a championship standing in the GridPilot platform.
|
||||
* Immutable entity with factory methods and domain validation.
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
export class Standing {
|
||||
export class Standing implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly leagueId: string;
|
||||
readonly driverId: string;
|
||||
readonly points: number;
|
||||
readonly wins: number;
|
||||
readonly position: number;
|
||||
readonly racesCompleted: number;
|
||||
|
||||
|
||||
private constructor(props: {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
points: number;
|
||||
@@ -23,6 +26,7 @@ export class Standing {
|
||||
position: number;
|
||||
racesCompleted: number;
|
||||
}) {
|
||||
this.id = props.id;
|
||||
this.leagueId = props.leagueId;
|
||||
this.driverId = props.driverId;
|
||||
this.points = props.points;
|
||||
@@ -35,6 +39,7 @@ export class Standing {
|
||||
* Factory method to create a new Standing entity
|
||||
*/
|
||||
static create(props: {
|
||||
id?: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
points?: number;
|
||||
@@ -44,7 +49,12 @@ export class Standing {
|
||||
}): Standing {
|
||||
this.validate(props);
|
||||
|
||||
const id = props.id && props.id.trim().length > 0
|
||||
? props.id
|
||||
: `${props.leagueId}:${props.driverId}`;
|
||||
|
||||
return new Standing({
|
||||
id,
|
||||
leagueId: props.leagueId,
|
||||
driverId: props.driverId,
|
||||
points: props.points ?? 0,
|
||||
@@ -58,15 +68,16 @@ export class Standing {
|
||||
* Domain validation logic
|
||||
*/
|
||||
private static validate(props: {
|
||||
id?: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
}): void {
|
||||
if (!props.leagueId || props.leagueId.trim().length === 0) {
|
||||
throw new RacingDomainError('League ID is required');
|
||||
throw new RacingDomainValidationError('League ID is required');
|
||||
}
|
||||
|
||||
|
||||
if (!props.driverId || props.driverId.trim().length === 0) {
|
||||
throw new RacingDomainError('Driver ID is required');
|
||||
throw new RacingDomainValidationError('Driver ID is required');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +89,7 @@ export class Standing {
|
||||
const isWin = position === 1;
|
||||
|
||||
return new Standing({
|
||||
id: this.id,
|
||||
leagueId: this.leagueId,
|
||||
driverId: this.driverId,
|
||||
points: this.points + racePoints,
|
||||
|
||||
@@ -1,35 +1,128 @@
|
||||
/**
|
||||
* Domain Entities: Team, TeamMembership, TeamJoinRequest
|
||||
* Domain Entity: Team
|
||||
*
|
||||
* Extracted from racing-application teams module so that
|
||||
* team-related types live in the racing-domain package.
|
||||
* Represents a racing team in the GridPilot platform.
|
||||
* Implements the shared IEntity<string> contract and encapsulates
|
||||
* basic invariants around identity and core properties.
|
||||
*/
|
||||
|
||||
export type TeamRole = 'owner' | 'manager' | 'driver';
|
||||
export type TeamMembershipStatus = 'active' | 'pending' | 'none';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
|
||||
export interface Team {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
createdAt: Date;
|
||||
}
|
||||
export class Team implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly tag: string;
|
||||
readonly description: string;
|
||||
readonly ownerId: string;
|
||||
readonly leagues: string[];
|
||||
readonly createdAt: Date;
|
||||
|
||||
export interface TeamMembership {
|
||||
teamId: string;
|
||||
driverId: string;
|
||||
role: TeamRole;
|
||||
status: TeamMembershipStatus;
|
||||
joinedAt: Date;
|
||||
}
|
||||
private constructor(props: {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
createdAt: Date;
|
||||
}) {
|
||||
this.id = props.id;
|
||||
this.name = props.name;
|
||||
this.tag = props.tag;
|
||||
this.description = props.description;
|
||||
this.ownerId = props.ownerId;
|
||||
this.leagues = props.leagues;
|
||||
this.createdAt = props.createdAt;
|
||||
}
|
||||
|
||||
export interface TeamJoinRequest {
|
||||
id: string;
|
||||
teamId: string;
|
||||
driverId: string;
|
||||
requestedAt: Date;
|
||||
message?: string;
|
||||
/**
|
||||
* Factory method to create a new Team entity.
|
||||
*/
|
||||
static create(props: {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
createdAt?: Date;
|
||||
}): Team {
|
||||
this.validate(props);
|
||||
|
||||
return new Team({
|
||||
id: props.id,
|
||||
name: props.name,
|
||||
tag: props.tag,
|
||||
description: props.description,
|
||||
ownerId: props.ownerId,
|
||||
leagues: [...props.leagues],
|
||||
createdAt: props.createdAt ?? new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy with updated properties.
|
||||
*/
|
||||
update(props: Partial<{
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
}>): Team {
|
||||
const next: Team = new Team({
|
||||
id: this.id,
|
||||
name: props.name ?? this.name,
|
||||
tag: props.tag ?? this.tag,
|
||||
description: props.description ?? this.description,
|
||||
ownerId: props.ownerId ?? this.ownerId,
|
||||
leagues: props.leagues ? [...props.leagues] : [...this.leagues],
|
||||
createdAt: this.createdAt,
|
||||
});
|
||||
|
||||
// Re-validate updated aggregate
|
||||
Team.validate({
|
||||
id: next.id,
|
||||
name: next.name,
|
||||
tag: next.tag,
|
||||
description: next.description,
|
||||
ownerId: next.ownerId,
|
||||
leagues: next.leagues,
|
||||
});
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Domain validation logic for core invariants.
|
||||
*/
|
||||
private static validate(props: {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
}): void {
|
||||
if (!props.id || props.id.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Team ID is required');
|
||||
}
|
||||
|
||||
if (!props.name || props.name.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Team name is required');
|
||||
}
|
||||
|
||||
if (!props.tag || props.tag.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Team tag is required');
|
||||
}
|
||||
|
||||
if (!props.ownerId || props.ownerId.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Team owner ID is required');
|
||||
}
|
||||
|
||||
if (!Array.isArray(props.leagues)) {
|
||||
throw new RacingDomainValidationError('Team leagues must be an array');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,14 @@
|
||||
* Represents a racing track/circuit in the GridPilot platform.
|
||||
* Immutable entity with factory methods and domain validation.
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
export type TrackCategory = 'oval' | 'road' | 'street' | 'dirt';
|
||||
export type TrackDifficulty = 'beginner' | 'intermediate' | 'advanced' | 'expert';
|
||||
|
||||
export class Track {
|
||||
export class Track implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly shortName: string;
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
*
|
||||
* Represents a financial transaction in the league wallet system.
|
||||
*/
|
||||
|
||||
|
||||
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
||||
|
||||
|
||||
import type { Money } from '../value-objects/Money';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
export type TransactionType =
|
||||
| 'sponsorship_payment'
|
||||
@@ -30,8 +31,8 @@ export interface TransactionProps {
|
||||
description?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export class Transaction {
|
||||
|
||||
export class Transaction implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly walletId: string;
|
||||
readonly type: TransactionType;
|
||||
|
||||
Reference in New Issue
Block a user