harden business rules
This commit is contained in:
136
core/racing/domain/value-objects/SeasonStatus.ts
Normal file
136
core/racing/domain/value-objects/SeasonStatus.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Domain Value Object: SeasonStatus
|
||||
*
|
||||
* Represents the status of a season with strict lifecycle rules.
|
||||
*/
|
||||
|
||||
import type { IValueObject } from '@core/shared/domain';
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
|
||||
export type SeasonStatusValue = 'planned' | 'active' | 'completed' | 'archived' | 'cancelled';
|
||||
|
||||
export interface SeasonStatusProps {
|
||||
value: SeasonStatusValue;
|
||||
}
|
||||
|
||||
export class SeasonStatus implements IValueObject<SeasonStatusProps> {
|
||||
readonly value: SeasonStatusValue;
|
||||
|
||||
private constructor(value: SeasonStatusValue) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
static create(value: SeasonStatusValue): SeasonStatus {
|
||||
if (!value) {
|
||||
throw new RacingDomainValidationError('Season status is required');
|
||||
}
|
||||
return new SeasonStatus(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if season can be activated
|
||||
*/
|
||||
canActivate(): boolean {
|
||||
return this.value === 'planned';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if season can be completed
|
||||
*/
|
||||
canComplete(): boolean {
|
||||
return this.value === 'active';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if season can be archived
|
||||
*/
|
||||
canArchive(): boolean {
|
||||
return this.value === 'completed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if season can be cancelled
|
||||
*/
|
||||
canCancel(): boolean {
|
||||
return this.value === 'planned' || this.value === 'active';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if season is in a terminal state
|
||||
*/
|
||||
isTerminal(): boolean {
|
||||
return this.value === 'completed' || this.value === 'archived' || this.value === 'cancelled';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if season is active
|
||||
*/
|
||||
isActive(): boolean {
|
||||
return this.value === 'active';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if season is completed
|
||||
*/
|
||||
isCompleted(): boolean {
|
||||
return this.value === 'completed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if season is planned
|
||||
*/
|
||||
isPlanned(): boolean {
|
||||
return this.value === 'planned';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if season is archived
|
||||
*/
|
||||
isArchived(): boolean {
|
||||
return this.value === 'archived';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if season is cancelled
|
||||
*/
|
||||
isCancelled(): boolean {
|
||||
return this.value === 'cancelled';
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate transition from current status to target status
|
||||
*/
|
||||
canTransitionTo(target: SeasonStatusValue): { valid: boolean; error?: string } {
|
||||
const current = this.value;
|
||||
|
||||
// Define allowed transitions
|
||||
const allowedTransitions: Record<SeasonStatusValue, SeasonStatusValue[]> = {
|
||||
planned: ['active', 'cancelled'],
|
||||
active: ['completed', 'cancelled'],
|
||||
completed: ['archived'],
|
||||
archived: [],
|
||||
cancelled: [],
|
||||
};
|
||||
|
||||
if (!allowedTransitions[current].includes(target)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Cannot transition from ${current} to ${target}`
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
get props(): SeasonStatusProps {
|
||||
return { value: this.value };
|
||||
}
|
||||
|
||||
equals(other: IValueObject<SeasonStatusProps>): boolean {
|
||||
return this.value === other.props.value;
|
||||
}
|
||||
|
||||
toString(): SeasonStatusValue {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user