refactor use cases

This commit is contained in:
2026-01-08 15:34:51 +01:00
parent d984ab24a8
commit 52e9a2f6a7
362 changed files with 5192 additions and 8409 deletions

View File

@@ -26,20 +26,9 @@ import { UpdateTeamUseCase, UpdateTeamInput } from '@core/racing/application/use
import { GetDriverTeamUseCase } from '@core/racing/application/use-cases/GetDriverTeamUseCase';
import { GetTeamMembershipUseCase } from '@core/racing/application/use-cases/GetTeamMembershipUseCase';
// API Presenters
import { AllTeamsPresenter } from './presenters/AllTeamsPresenter';
import { TeamDetailsPresenter } from './presenters/TeamDetailsPresenter';
import { TeamMembersPresenter } from './presenters/TeamMembersPresenter';
import { TeamJoinRequestsPresenter } from './presenters/TeamJoinRequestsPresenter';
import { DriverTeamPresenter } from './presenters/DriverTeamPresenter';
import { TeamMembershipPresenter } from './presenters/TeamMembershipPresenter';
import { CreateTeamPresenter } from './presenters/CreateTeamPresenter';
import { UpdateTeamPresenter } from './presenters/UpdateTeamPresenter';
// Tokens
import { TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN, LOGGER_TOKEN, TEAM_STATS_REPOSITORY_TOKEN, RESULT_REPOSITORY_TOKEN } from './TeamTokens';
import { TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN, LOGGER_TOKEN, TEAM_STATS_REPOSITORY_TOKEN } from './TeamTokens';
import type { ITeamStatsRepository } from '@core/racing/domain/repositories/ITeamStatsRepository';
import type { IResultRepository } from '@core/racing/domain/repositories/IResultRepository';
@Injectable()
export class TeamService {
@@ -49,8 +38,6 @@ export class TeamService {
@Inject(DRIVER_REPOSITORY_TOKEN) private readonly driverRepository: IDriverRepository,
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
@Inject(TEAM_STATS_REPOSITORY_TOKEN) private readonly teamStatsRepository: ITeamStatsRepository,
@Inject(RESULT_REPOSITORY_TOKEN) private readonly resultRepository: IResultRepository,
private readonly allTeamsPresenter: AllTeamsPresenter,
) {}
async getAll(): Promise<GetAllTeamsOutputDTO> {
@@ -60,38 +47,82 @@ export class TeamService {
this.teamRepository,
this.membershipRepository,
this.teamStatsRepository,
this.resultRepository,
this.logger,
this.allTeamsPresenter
this.logger
);
const result = await useCase.execute();
const result = await useCase.execute({});
if (result.isErr()) {
this.logger.error('Error fetching all teams', new Error(result.error?.details?.message || 'Unknown error'));
return { teams: [], totalCount: 0 };
}
return this.allTeamsPresenter.getResponseModel()!;
const value = result.value;
if (!value) {
return { teams: [], totalCount: 0 };
}
return {
teams: value.teams.map(t => ({
id: t.team.id,
name: t.team.name.toString(),
tag: t.team.tag.toString(),
description: t.description,
memberCount: t.memberCount,
leagues: t.leagues,
totalWins: t.totalWins,
totalRaces: t.totalRaces,
performanceLevel: t.performanceLevel,
specialization: t.specialization,
region: t.region,
languages: t.languages,
rating: t.rating,
logoUrl: t.logoUrl,
isRecruiting: t.isRecruiting,
})),
totalCount: value.totalCount,
};
}
async getDetails(teamId: string, userId?: string): Promise<GetTeamDetailsOutputDTO | null> {
this.logger.debug(`[TeamService] Fetching team details for teamId: ${teamId}, userId: ${userId}`);
const presenter = new TeamDetailsPresenter();
const useCase = new GetTeamDetailsUseCase(this.teamRepository, this.membershipRepository, presenter);
const useCase = new GetTeamDetailsUseCase(this.teamRepository, this.membershipRepository);
const result = await useCase.execute({ teamId, driverId: userId || '' });
if (result.isErr()) {
this.logger.error(`Error fetching team details for teamId: ${teamId}: ${result.error?.details?.message || 'Unknown error'}`);
return null;
}
return presenter.getResponseModel()!;
const value = result.value;
if (!value) {
return null;
}
// Convert to DTO
return {
team: {
id: value.team.id,
name: value.team.name.toString(),
tag: value.team.tag.toString(),
description: value.team.description.toString(),
ownerId: value.team.ownerId.toString(),
leagues: value.team.leagues.map(l => l.toString()),
isRecruiting: value.team.isRecruiting,
createdAt: value.team.createdAt?.toDate()?.toISOString?.() || new Date().toISOString(),
category: undefined,
},
membership: value.membership ? {
role: value.membership.role === 'driver' ? 'member' : (value.membership.role as 'owner' | 'manager' | 'member'),
joinedAt: value.membership.joinedAt.toISOString(),
isActive: value.membership.status === 'active',
} : null,
canManage: value.canManage,
};
}
async getMembers(teamId: string): Promise<GetTeamMembersOutputDTO> {
this.logger.debug(`[TeamService] Fetching team members for teamId: ${teamId}`);
const presenter = new TeamMembersPresenter();
const useCase = new GetTeamMembersUseCase(this.membershipRepository, this.driverRepository, this.teamRepository, this.logger, presenter);
const useCase = new GetTeamMembersUseCase(this.membershipRepository, this.driverRepository, this.teamRepository, this.logger);
const result = await useCase.execute({ teamId });
if (result.isErr()) {
this.logger.error(`Error fetching team members for teamId: ${teamId}: ${result.error?.details?.message || 'Unknown error'}`);
@@ -104,14 +135,37 @@ export class TeamService {
};
}
return presenter.getResponseModel()!;
const value = result.value;
if (!value) {
return {
members: [],
totalCount: 0,
ownerCount: 0,
managerCount: 0,
memberCount: 0,
};
}
return {
members: value.members.map(m => ({
driverId: m.driver?.id || '',
driverName: m.driver?.name?.toString() || '',
role: m.membership.role === 'driver' ? 'member' : (m.membership.role as 'owner' | 'manager' | 'member'),
joinedAt: m.membership.joinedAt.toISOString(),
isActive: m.membership.status === 'active',
avatarUrl: '', // Would need MediaResolver here
})),
totalCount: value.members.length,
ownerCount: value.members.filter(m => m.membership.role === 'owner').length,
managerCount: value.members.filter(m => m.membership.role === 'manager').length,
memberCount: value.members.filter(m => m.membership.role === 'driver').length,
};
}
async getJoinRequests(teamId: string): Promise<GetTeamJoinRequestsOutputDTO> {
this.logger.debug(`[TeamService] Fetching team join requests for teamId: ${teamId}`);
const presenter = new TeamJoinRequestsPresenter();
const useCase = new GetTeamJoinRequestsUseCase(this.membershipRepository, this.driverRepository, this.teamRepository, presenter);
const useCase = new GetTeamJoinRequestsUseCase(this.membershipRepository, this.driverRepository, this.teamRepository);
const result = await useCase.execute({ teamId });
if (result.isErr()) {
this.logger.error(`Error fetching team join requests for teamId: ${teamId}`, new Error(result.error?.details?.message || 'Unknown error'));
@@ -122,14 +176,33 @@ export class TeamService {
};
}
return presenter.getResponseModel()!;
const value = result.value;
if (!value) {
return {
requests: [],
pendingCount: 0,
totalCount: 0,
};
}
return {
requests: value.joinRequests.map(r => ({
requestId: r.id,
driverId: r.driverId,
driverName: r.driver.name.toString(),
teamId: r.teamId,
status: 'pending',
requestedAt: r.requestedAt.toISOString(),
avatarUrl: '', // Would need MediaResolver here
})),
pendingCount: value.joinRequests.length,
totalCount: value.joinRequests.length,
};
}
async create(input: CreateTeamInputDTO, userId?: string): Promise<CreateTeamOutputDTO> {
this.logger.debug('[TeamService] Creating team', { input, userId });
const presenter = new CreateTeamPresenter();
const command: CreateTeamInput = {
name: input.name,
tag: input.tag,
@@ -138,21 +211,24 @@ export class TeamService {
leagues: [],
};
const useCase = new CreateTeamUseCase(this.teamRepository, this.membershipRepository, this.logger, presenter);
const useCase = new CreateTeamUseCase(this.teamRepository, this.membershipRepository, this.logger);
const result = await useCase.execute(command);
if (result.isErr()) {
this.logger.error(`Error creating team: ${result.error?.details?.message || 'Unknown error'}`);
return { id: '', success: false };
}
return presenter.responseModel;
const value = result.value;
if (!value) {
return { id: '', success: false };
}
return { id: value.team.id, success: true };
}
async update(teamId: string, input: UpdateTeamInputDTO, userId?: string): Promise<UpdateTeamOutputDTO> {
this.logger.debug(`[TeamService] Updating team ${teamId}`, { input, userId });
const presenter = new UpdateTeamPresenter();
const command: UpdateTeamInput = {
teamId,
updates: {
@@ -163,41 +239,72 @@ export class TeamService {
updatedBy: userId || '',
};
const useCase = new UpdateTeamUseCase(this.teamRepository, this.membershipRepository, presenter);
const useCase = new UpdateTeamUseCase(this.teamRepository, this.membershipRepository);
const result = await useCase.execute(command);
if (result.isErr()) {
this.logger.error(`Error updating team ${teamId}: ${result.error?.details?.message || 'Unknown error'}`);
return { success: false };
}
return presenter.responseModel;
return { success: true };
}
async getDriverTeam(driverId: string): Promise<GetDriverTeamOutputDTO | null> {
this.logger.debug(`[TeamService] Fetching team for driverId: ${driverId}`);
const presenter = new DriverTeamPresenter();
const useCase = new GetDriverTeamUseCase(this.teamRepository, this.membershipRepository, this.logger, presenter);
const useCase = new GetDriverTeamUseCase(this.teamRepository, this.membershipRepository, this.logger);
const result = await useCase.execute({ driverId });
if (result.isErr()) {
this.logger.error(`Error fetching team for driverId: ${driverId}: ${result.error?.details?.message || 'Unknown error'}`);
return null;
}
return presenter.getResponseModel();
const value = result.value;
if (!value || !value.team) {
return null;
}
return {
team: {
id: value.team.id,
name: value.team.name.toString(),
tag: value.team.tag.toString(),
description: value.team.description.toString(),
ownerId: value.team.ownerId.toString(),
leagues: value.team.leagues.map(l => l.toString()),
isRecruiting: value.team.isRecruiting,
createdAt: value.team.createdAt?.toDate?.()?.toISOString?.() || new Date().toISOString(),
category: undefined,
},
membership: {
role: value.membership.role === 'driver' ? 'member' : (value.membership.role as 'owner' | 'manager' | 'member'),
joinedAt: value.membership.joinedAt.toISOString(),
isActive: value.membership.status === 'active',
},
isOwner: value.membership.role === 'owner',
canManage: value.membership.role === 'owner' || value.membership.role === 'manager',
};
}
async getMembership(teamId: string, driverId: string): Promise<GetTeamMembershipOutputDTO | null> {
this.logger.debug(`[TeamService] Fetching team membership for teamId: ${teamId}, driverId: ${driverId}`);
const presenter = new TeamMembershipPresenter();
const useCase = new GetTeamMembershipUseCase(this.membershipRepository, this.logger, presenter);
const useCase = new GetTeamMembershipUseCase(this.membershipRepository, this.logger);
const result = await useCase.execute({ teamId, driverId });
if (result.isErr()) {
this.logger.error(`Error fetching team membership for teamId: ${teamId}, driverId: ${driverId}: ${result.error?.details?.message || 'Unknown error'}`);
return null;
}
return presenter.getResponseModel();
const value = result.value;
if (!value) {
return null;
}
return value.membership ? {
role: value.membership.role,
joinedAt: value.membership.joinedAt,
isActive: value.membership.isActive,
} : null;
}
}