refactor racing use cases
This commit is contained in:
@@ -1,22 +1,19 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { League } from '../../domain/entities/League';
|
||||
import { Season } from '../../domain/entities/Season';
|
||||
import { Season } from '../../domain/entities/season/Season';
|
||||
import { LeagueScoringConfig } from '../../domain/entities/LeagueScoringConfig';
|
||||
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
|
||||
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
|
||||
import type { ILeagueScoringConfigRepository } from '../../domain/repositories/ILeagueScoringConfigRepository';
|
||||
import type { AsyncUseCase , Logger } from '@core/shared/application';
|
||||
import type { GetLeagueScoringPresetByIdInputPort } from '../ports/input/GetLeagueScoringPresetByIdInputPort';
|
||||
import type { LeagueScoringPresetOutputPort } from '../ports/output/LeagueScoringPresetOutputPort';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import {
|
||||
LeagueVisibility,
|
||||
MIN_RANKED_LEAGUE_DRIVERS,
|
||||
} from '../../domain/value-objects/LeagueVisibility';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import type { LeagueVisibilityInput } from '../dto/LeagueVisibilityInput';
|
||||
import type { CreateLeagueWithSeasonAndScoringOutputPort } from '../ports/output/CreateLeagueWithSeasonAndScoringOutputPort';
|
||||
|
||||
export interface CreateLeagueWithSeasonAndScoringCommand {
|
||||
export type CreateLeagueWithSeasonAndScoringCommand = {
|
||||
name: string;
|
||||
description?: string;
|
||||
/**
|
||||
@@ -24,7 +21,7 @@ export interface CreateLeagueWithSeasonAndScoringCommand {
|
||||
* - 'ranked' (or legacy 'public'): Competitive, public, affects ratings. Requires min 10 drivers.
|
||||
* - 'unranked' (or legacy 'private'): Casual with friends, no rating impact.
|
||||
*/
|
||||
visibility: LeagueVisibilityInput;
|
||||
visibility: string;
|
||||
ownerId: string;
|
||||
gameId: string;
|
||||
maxDrivers?: number;
|
||||
@@ -34,21 +31,37 @@ export interface CreateLeagueWithSeasonAndScoringCommand {
|
||||
enableNationsChampionship: boolean;
|
||||
enableTrophyChampionship: boolean;
|
||||
scoringPresetId?: string;
|
||||
}
|
||||
};
|
||||
|
||||
export class CreateLeagueWithSeasonAndScoringUseCase
|
||||
implements AsyncUseCase<CreateLeagueWithSeasonAndScoringCommand, CreateLeagueWithSeasonAndScoringOutputPort, 'VALIDATION_ERROR' | 'UNKNOWN_PRESET' | 'REPOSITORY_ERROR'> {
|
||||
export type CreateLeagueWithSeasonAndScoringResult = {
|
||||
league: League;
|
||||
season: Season;
|
||||
scoringConfig: LeagueScoringConfig;
|
||||
};
|
||||
|
||||
type CreateLeagueWithSeasonAndScoringErrorCode =
|
||||
| 'VALIDATION_ERROR'
|
||||
| 'UNKNOWN_PRESET'
|
||||
| 'REPOSITORY_ERROR';
|
||||
|
||||
type ScoringPreset = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export class CreateLeagueWithSeasonAndScoringUseCase {
|
||||
constructor(
|
||||
private readonly leagueRepository: ILeagueRepository,
|
||||
private readonly seasonRepository: ISeasonRepository,
|
||||
private readonly leagueScoringConfigRepository: ILeagueScoringConfigRepository,
|
||||
private readonly getLeagueScoringPresetById: (input: GetLeagueScoringPresetByIdInputPort) => Promise<LeagueScoringPresetOutputPort | undefined>,
|
||||
private readonly getLeagueScoringPresetById: (input: { presetId: string }) => Promise<ScoringPreset | undefined>,
|
||||
private readonly logger: Logger,
|
||||
private readonly output: UseCaseOutputPort<CreateLeagueWithSeasonAndScoringResult>,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
command: CreateLeagueWithSeasonAndScoringCommand,
|
||||
): Promise<Result<CreateLeagueWithSeasonAndScoringOutputPort, ApplicationErrorCode<'VALIDATION_ERROR' | 'UNKNOWN_PRESET' | 'REPOSITORY_ERROR', { message: string }>>> {
|
||||
): Promise<Result<void, ApplicationErrorCode<CreateLeagueWithSeasonAndScoringErrorCode>>> {
|
||||
this.logger.debug('Executing CreateLeagueWithSeasonAndScoringUseCase', { command });
|
||||
const validation = this.validate(command);
|
||||
if (validation.isErr()) {
|
||||
@@ -93,8 +106,7 @@ export class CreateLeagueWithSeasonAndScoringUseCase
|
||||
|
||||
const presetId = command.scoringPresetId ?? 'club-default';
|
||||
this.logger.debug(`Attempting to retrieve scoring preset: ${presetId}`);
|
||||
const preset: LeagueScoringPresetOutputPort | undefined =
|
||||
await this.getLeagueScoringPresetById({ presetId });
|
||||
const preset = await this.getLeagueScoringPresetById({ presetId });
|
||||
|
||||
if (!preset) {
|
||||
this.logger.error(`Unknown scoring preset: ${presetId}`);
|
||||
@@ -102,10 +114,7 @@ export class CreateLeagueWithSeasonAndScoringUseCase
|
||||
}
|
||||
this.logger.info(`Scoring preset ${preset.name} (${preset.id}) retrieved.`);
|
||||
|
||||
// Note: createScoringConfigFromPreset business logic should be moved to domain layer
|
||||
// For now, we'll create a basic config structure
|
||||
const finalConfig = {
|
||||
id: uuidv4(),
|
||||
const scoringConfig = LeagueScoringConfig.create({
|
||||
seasonId,
|
||||
scoringPresetId: preset.id,
|
||||
championships: {
|
||||
@@ -114,26 +123,33 @@ export class CreateLeagueWithSeasonAndScoringUseCase
|
||||
nations: command.enableNationsChampionship,
|
||||
trophy: command.enableTrophyChampionship,
|
||||
},
|
||||
};
|
||||
});
|
||||
this.logger.debug(`Scoring configuration created from preset ${preset.id}.`);
|
||||
|
||||
await this.leagueScoringConfigRepository.save(finalConfig);
|
||||
await this.leagueScoringConfigRepository.save(scoringConfig);
|
||||
this.logger.info(`Scoring configuration saved for season ${seasonId}.`);
|
||||
|
||||
const result: CreateLeagueWithSeasonAndScoringOutputPort = {
|
||||
leagueId: league.id.toString(),
|
||||
seasonId,
|
||||
scoringPresetId: preset.id,
|
||||
scoringPresetName: preset.name,
|
||||
const result: CreateLeagueWithSeasonAndScoringResult = {
|
||||
league,
|
||||
season,
|
||||
scoringConfig,
|
||||
};
|
||||
this.logger.debug('CreateLeagueWithSeasonAndScoringUseCase completed successfully.', { result });
|
||||
return Result.ok(result);
|
||||
this.output.present(result);
|
||||
return Result.ok(undefined);
|
||||
} catch (error) {
|
||||
return Result.err({ code: 'REPOSITORY_ERROR', details: { message: error instanceof Error ? error.message : 'Unknown error' } });
|
||||
return Result.err({
|
||||
code: 'REPOSITORY_ERROR',
|
||||
details: {
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private validate(command: CreateLeagueWithSeasonAndScoringCommand): Result<void, ApplicationErrorCode<'VALIDATION_ERROR', { message: string }>> {
|
||||
private validate(
|
||||
command: CreateLeagueWithSeasonAndScoringCommand,
|
||||
): Result<void, ApplicationErrorCode<'VALIDATION_ERROR', { message: string }>> {
|
||||
this.logger.debug('Validating CreateLeagueWithSeasonAndScoringCommand', { command });
|
||||
if (!command.name || command.name.trim().length === 0) {
|
||||
this.logger.warn('Validation failed: League name is required', { command });
|
||||
|
||||
Reference in New Issue
Block a user