harden business rules
This commit is contained in:
121
core/racing/domain/value-objects/ParticipantCount.ts
Normal file
121
core/racing/domain/value-objects/ParticipantCount.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Domain Value Object: ParticipantCount
|
||||
*
|
||||
* Represents the number of participants in a league or race.
|
||||
* Enforces constraints based on league visibility and other business rules.
|
||||
*/
|
||||
|
||||
import type { IValueObject } from '@core/shared/domain';
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
|
||||
export interface ParticipantCountProps {
|
||||
value: number;
|
||||
}
|
||||
|
||||
export class ParticipantCount implements IValueObject<ParticipantCountProps> {
|
||||
readonly value: number;
|
||||
|
||||
private constructor(value: number) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
static create(value: number): ParticipantCount {
|
||||
if (!Number.isInteger(value) || value < 0) {
|
||||
throw new RacingDomainValidationError('Participant count must be a non-negative integer');
|
||||
}
|
||||
return new ParticipantCount(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate against minimum requirements for ranked leagues
|
||||
*/
|
||||
validateForRankedLeague(): { valid: boolean; error?: string } {
|
||||
if (this.value < 10) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Ranked leagues require at least 10 participants'
|
||||
};
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate against minimum requirements for unranked leagues
|
||||
*/
|
||||
validateForUnrankedLeague(): { valid: boolean; error?: string } {
|
||||
if (this.value < 2) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Unranked leagues require at least 2 participants'
|
||||
};
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate against maximum capacity
|
||||
*/
|
||||
validateAgainstMax(maxParticipants: number): { valid: boolean; error?: string } {
|
||||
if (this.value > maxParticipants) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Participant count (${this.value}) exceeds maximum capacity (${maxParticipants})`
|
||||
};
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if count meets minimum for given visibility type
|
||||
*/
|
||||
meetsMinimumForVisibility(isRanked: boolean): boolean {
|
||||
return isRanked ? this.value >= 10 : this.value >= 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment count by 1
|
||||
*/
|
||||
increment(): ParticipantCount {
|
||||
return new ParticipantCount(this.value + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement count by 1 (if > 0)
|
||||
*/
|
||||
decrement(): ParticipantCount {
|
||||
if (this.value === 0) {
|
||||
throw new RacingDomainValidationError('Cannot decrement below zero');
|
||||
}
|
||||
return new ParticipantCount(this.value - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if count is zero
|
||||
*/
|
||||
isZero(): boolean {
|
||||
return this.value === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if count is at least the given minimum
|
||||
*/
|
||||
isAtLeast(min: number): boolean {
|
||||
return this.value >= min;
|
||||
}
|
||||
|
||||
get props(): ParticipantCountProps {
|
||||
return { value: this.value };
|
||||
}
|
||||
|
||||
equals(other: IValueObject<ParticipantCountProps>): boolean {
|
||||
return this.value === other.props.value;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.value.toString();
|
||||
}
|
||||
|
||||
toNumber(): number {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user