presenter refactoring

This commit is contained in:
2025-12-20 17:06:11 +01:00
parent 92be9d2e1b
commit e9d6f90bb2
109 changed files with 4159 additions and 1283 deletions

View File

@@ -1,12 +1,6 @@
import { Injectable, Inject } from '@nestjs/common';
import { DriversLeaderboardDTO } from './dtos/DriversLeaderboardDTO';
import { DriverStatsDTO } from './dtos/DriverStatsDTO';
import { CompleteOnboardingInputDTO } from './dtos/CompleteOnboardingInputDTO';
import { CompleteOnboardingOutputDTO } from './dtos/CompleteOnboardingOutputDTO';
import { GetDriverRegistrationStatusQueryDTO } from './dtos/GetDriverRegistrationStatusQueryDTO';
import { DriverRegistrationStatusDTO } from './dtos/DriverRegistrationStatusDTO';
import { GetDriverOutputDTO } from './dtos/GetDriverOutputDTO';
import { GetDriverProfileOutputDTO } from './dtos/GetDriverProfileOutputDTO';
// Use cases
import { GetDriversLeaderboardUseCase } from '@core/racing/application/use-cases/GetDriversLeaderboardUseCase';
@@ -14,42 +8,66 @@ import { GetTotalDriversUseCase } from '@core/racing/application/use-cases/GetTo
import { CompleteDriverOnboardingUseCase } from '@core/racing/application/use-cases/CompleteDriverOnboardingUseCase';
import { IsDriverRegisteredForRaceUseCase } from '@core/racing/application/use-cases/IsDriverRegisteredForRaceUseCase';
import { GetProfileOverviewUseCase } from '@core/racing/application/use-cases/GetProfileOverviewUseCase';
import { UpdateDriverProfileUseCase } from '@core/racing/application/use-cases/UpdateDriverProfileUseCase';
// Presenters
import { DriverStatsPresenter } from './presenters/DriverStatsPresenter';
import { DriversLeaderboardPresenter } from './presenters/DriversLeaderboardPresenter';
import { CompleteOnboardingPresenter } from './presenters/CompleteOnboardingPresenter';
import { DriverRegistrationStatusPresenter } from './presenters/DriverRegistrationStatusPresenter';
import { DriverPresenter } from './presenters/DriverPresenter';
import { DriverProfilePresenter } from './presenters/DriverProfilePresenter';
// Tokens
import { GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN, GET_TOTAL_DRIVERS_USE_CASE_TOKEN, COMPLETE_DRIVER_ONBOARDING_USE_CASE_TOKEN, IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN, UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN, GET_PROFILE_OVERVIEW_USE_CASE_TOKEN, LOGGER_TOKEN, DRIVER_REPOSITORY_TOKEN } from './DriverProviders';
import type { ProfileOverviewOutputPort } from '@core/racing/application/ports/output/ProfileOverviewOutputPort';
import {
GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN,
GET_TOTAL_DRIVERS_USE_CASE_TOKEN,
COMPLETE_DRIVER_ONBOARDING_USE_CASE_TOKEN,
IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN,
UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN,
GET_PROFILE_OVERVIEW_USE_CASE_TOKEN,
LOGGER_TOKEN,
DRIVER_REPOSITORY_TOKEN,
} from './DriverProviders';
import type { Logger } from '@core/shared/application';
import { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
import { UpdateDriverProfileUseCase } from '@core/racing/application/use-cases/UpdateDriverProfileUseCase';
@Injectable()
export class DriverService {
constructor(
@Inject(GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN) private readonly getDriversLeaderboardUseCase: GetDriversLeaderboardUseCase,
@Inject(GET_TOTAL_DRIVERS_USE_CASE_TOKEN) private readonly getTotalDriversUseCase: GetTotalDriversUseCase,
@Inject(COMPLETE_DRIVER_ONBOARDING_USE_CASE_TOKEN) private readonly completeDriverOnboardingUseCase: CompleteDriverOnboardingUseCase,
@Inject(IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN) private readonly isDriverRegisteredForRaceUseCase: IsDriverRegisteredForRaceUseCase,
@Inject(UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN) private readonly updateDriverProfileUseCase: UpdateDriverProfileUseCase,
@Inject(GET_PROFILE_OVERVIEW_USE_CASE_TOKEN) private readonly getProfileOverviewUseCase: GetProfileOverviewUseCase,
@Inject(DRIVER_REPOSITORY_TOKEN) private readonly driverRepository: IDriverRepository,
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
@Inject(GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN)
private readonly getDriversLeaderboardUseCase: GetDriversLeaderboardUseCase,
@Inject(GET_TOTAL_DRIVERS_USE_CASE_TOKEN)
private readonly getTotalDriversUseCase: GetTotalDriversUseCase,
@Inject(COMPLETE_DRIVER_ONBOARDING_USE_CASE_TOKEN)
private readonly completeDriverOnboardingUseCase: CompleteDriverOnboardingUseCase,
@Inject(IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN)
private readonly isDriverRegisteredForRaceUseCase: IsDriverRegisteredForRaceUseCase,
@Inject(UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN)
private readonly updateDriverProfileUseCase: UpdateDriverProfileUseCase,
@Inject(GET_PROFILE_OVERVIEW_USE_CASE_TOKEN)
private readonly getProfileOverviewUseCase: GetProfileOverviewUseCase,
@Inject(DRIVER_REPOSITORY_TOKEN)
private readonly driverRepository: IDriverRepository,
@Inject(LOGGER_TOKEN)
private readonly logger: Logger,
) {}
async getDriversLeaderboard(): Promise<DriversLeaderboardDTO> {
async getDriversLeaderboard(): Promise<DriversLeaderboardPresenter> {
this.logger.debug('[DriverService] Fetching drivers leaderboard.');
const result = await this.getDriversLeaderboardUseCase.execute();
if (result.isOk()) {
return result.value as DriversLeaderboardDTO;
} else {
throw new Error(`Failed to fetch drivers leaderboard: ${result.error.details.message}`);
if (result.isErr()) {
throw new Error(`Failed to fetch drivers leaderboard: ${result.unwrapErr().details.message}`);
}
const presenter = new DriversLeaderboardPresenter();
presenter.reset();
presenter.present(result.unwrap());
return presenter;
}
async getTotalDrivers(): Promise<DriverStatsDTO> {
async getTotalDrivers(): Promise<DriverStatsPresenter> {
this.logger.debug('[DriverService] Fetching total drivers count.');
const result = await this.getTotalDriversUseCase.execute();
@@ -58,11 +76,12 @@ export class DriverService {
}
const presenter = new DriverStatsPresenter();
presenter.reset();
presenter.present(result.unwrap());
return presenter.viewModel;
return presenter;
}
async completeOnboarding(userId: string, input: CompleteOnboardingInputDTO): Promise<CompleteOnboardingOutputDTO> {
async completeOnboarding(userId: string, input: CompleteOnboardingInputDTO): Promise<CompleteOnboardingPresenter> {
this.logger.debug('Completing onboarding for user:', userId);
const result = await this.completeDriverOnboardingUseCase.execute({
@@ -75,80 +94,88 @@ export class DriverService {
bio: input.bio,
});
const presenter = new CompleteOnboardingPresenter();
presenter.reset();
if (result.isOk()) {
return {
success: true,
driverId: result.value.driverId,
};
presenter.present(result.value);
} else {
return {
success: false,
errorMessage: result.error.code,
};
presenter.presentError(result.error.code);
}
return presenter;
}
async getDriverRegistrationStatus(query: GetDriverRegistrationStatusQueryDTO): Promise<DriverRegistrationStatusDTO> {
async getDriverRegistrationStatus(
query: GetDriverRegistrationStatusQueryDTO,
): Promise<DriverRegistrationStatusPresenter> {
this.logger.debug('Checking driver registration status:', query);
const result = await this.isDriverRegisteredForRaceUseCase.execute({ raceId: query.raceId, driverId: query.driverId });
if (result.isOk()) {
return result.value;
} else {
// For now, throw error or handle appropriately. Since it's a query, perhaps return default or throw.
throw new Error(`Failed to check registration status: ${result.error.code}`);
const result = await this.isDriverRegisteredForRaceUseCase.execute({
raceId: query.raceId,
driverId: query.driverId,
});
if (result.isErr()) {
throw new Error(`Failed to check registration status: ${result.unwrapErr().code}`);
}
const presenter = new DriverRegistrationStatusPresenter();
presenter.reset();
const output = result.unwrap();
presenter.present(output.isRegistered, output.raceId, output.driverId);
return presenter;
}
async getCurrentDriver(userId: string): Promise<GetDriverOutputDTO | null> {
async getCurrentDriver(userId: string): Promise<DriverPresenter> {
this.logger.debug(`[DriverService] Fetching current driver for userId: ${userId}`);
const driver = await this.driverRepository.findById(userId);
if (!driver) {
return null;
}
return {
id: driver.id,
iracingId: driver.iracingId.value,
name: driver.name.value,
country: driver.country.value,
bio: driver.bio?.value,
joinedAt: driver.joinedAt.toISOString(),
};
const presenter = new DriverPresenter();
presenter.reset();
presenter.present(driver ?? null);
return presenter;
}
async updateDriverProfile(driverId: string, bio?: string, country?: string): Promise<GetDriverOutputDTO | null> {
async updateDriverProfile(
driverId: string,
bio?: string,
country?: string,
): Promise<DriverPresenter> {
this.logger.debug(`[DriverService] Updating driver profile for driverId: ${driverId}`);
const result = await this.updateDriverProfileUseCase.execute({ driverId, bio, country });
const presenter = new DriverPresenter();
presenter.reset();
if (result.isErr()) {
this.logger.error(`Failed to update driver profile: ${result.error.code}`);
return null;
presenter.present(null);
return presenter;
}
return result.value;
presenter.present(result.value);
return presenter;
}
async getDriver(driverId: string): Promise<GetDriverOutputDTO | null> {
async getDriver(driverId: string): Promise<DriverPresenter> {
this.logger.debug(`[DriverService] Fetching driver for driverId: ${driverId}`);
const driver = await this.driverRepository.findById(driverId);
if (!driver) {
return null;
}
return {
id: driver.id,
iracingId: driver.iracingId.value,
name: driver.name.value,
country: driver.country.value,
bio: driver.bio?.value,
joinedAt: driver.joinedAt.toISOString(),
};
const presenter = new DriverPresenter();
presenter.reset();
presenter.present(driver ?? null);
return presenter;
}
async getDriverProfile(driverId: string): Promise<GetDriverProfileOutputDTO> {
async getDriverProfile(driverId: string): Promise<DriverProfilePresenter> {
this.logger.debug(`[DriverService] Fetching driver profile for driverId: ${driverId}`);
const result = await this.getProfileOverviewUseCase.execute({ driverId });
@@ -156,37 +183,10 @@ export class DriverService {
throw new Error(`Failed to fetch driver profile: ${result.error.code}`);
}
const outputPort = result.value;
return this.mapProfileOverviewToDTO(outputPort);
}
const presenter = new DriverProfilePresenter();
presenter.reset();
presenter.present(result.value);
private mapProfileOverviewToDTO(outputPort: ProfileOverviewOutputPort): GetDriverProfileOutputDTO {
return {
currentDriver: outputPort.driver ? {
id: outputPort.driver.id,
name: outputPort.driver.name,
country: outputPort.driver.country,
avatarUrl: outputPort.driver.avatarUrl,
iracingId: outputPort.driver.iracingId,
joinedAt: outputPort.driver.joinedAt.toISOString(),
rating: outputPort.driver.rating,
globalRank: outputPort.driver.globalRank,
consistency: outputPort.driver.consistency,
bio: outputPort.driver.bio,
totalDrivers: outputPort.driver.totalDrivers,
} : null,
stats: outputPort.stats,
finishDistribution: outputPort.finishDistribution,
teamMemberships: outputPort.teamMemberships.map(membership => ({
teamId: membership.teamId,
teamName: membership.teamName,
teamTag: membership.teamTag,
role: membership.role,
joinedAt: membership.joinedAt.toISOString(),
isCurrent: membership.isCurrent,
})),
socialSummary: outputPort.socialSummary,
extendedProfile: outputPort.extendedProfile,
};
return presenter;
}
}