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

@@ -22,7 +22,8 @@ export class TeamController {
@ApiOperation({ summary: 'Get all teams' })
@ApiResponse({ status: 200, description: 'List of all teams', type: GetAllTeamsOutputDTO })
async getAll(): Promise<GetAllTeamsOutputDTO> {
return this.teamService.getAll();
const presenter = await this.teamService.getAll();
return presenter.viewModel;
}
@Get(':teamId')
@@ -31,21 +32,24 @@ export class TeamController {
@ApiResponse({ status: 404, description: 'Team not found' })
async getDetails(@Param('teamId') teamId: string, @Req() req: Request): Promise<GetTeamDetailsOutputDTO | null> {
const userId = req['user']?.userId;
return this.teamService.getDetails(teamId, userId);
const presenter = await this.teamService.getDetails(teamId, userId);
return presenter.getViewModel();
}
@Get(':teamId/members')
@ApiOperation({ summary: 'Get team members' })
@ApiResponse({ status: 200, description: 'Team members', type: GetTeamMembersOutputDTO })
async getMembers(@Param('teamId') teamId: string): Promise<GetTeamMembersOutputDTO> {
return this.teamService.getMembers(teamId);
const presenter = await this.teamService.getMembers(teamId);
return presenter.getViewModel()!;
}
@Get(':teamId/join-requests')
@ApiOperation({ summary: 'Get team join requests' })
@ApiResponse({ status: 200, description: 'Team join requests', type: GetTeamJoinRequestsOutputDTO })
async getJoinRequests(@Param('teamId') teamId: string): Promise<GetTeamJoinRequestsOutputDTO> {
return this.teamService.getJoinRequests(teamId);
const presenter = await this.teamService.getJoinRequests(teamId);
return presenter.getViewModel()!;
}
@Post()
@@ -53,7 +57,8 @@ export class TeamController {
@ApiResponse({ status: 201, description: 'Team created', type: CreateTeamOutputDTO })
async create(@Body() input: CreateTeamInputDTO, @Req() req: Request): Promise<CreateTeamOutputDTO> {
const userId = req['user']?.userId;
return this.teamService.create(input, userId);
const presenter = await this.teamService.create(input, userId);
return presenter.viewModel;
}
@Patch(':teamId')
@@ -61,7 +66,8 @@ export class TeamController {
@ApiResponse({ status: 200, description: 'Team updated', type: UpdateTeamOutputDTO })
async update(@Param('teamId') teamId: string, @Body() input: UpdateTeamInputDTO, @Req() req: Request): Promise<UpdateTeamOutputDTO> {
const userId = req['user']?.userId;
return this.teamService.update(teamId, input, userId);
const presenter = await this.teamService.update(teamId, input, userId);
return presenter.viewModel;
}
@Get('driver/:driverId')
@@ -69,14 +75,16 @@ export class TeamController {
@ApiResponse({ status: 200, description: 'Driver\'s team', type: GetDriverTeamOutputDTO })
@ApiResponse({ status: 404, description: 'Team not found' })
async getDriverTeam(@Param('driverId') driverId: string): Promise<GetDriverTeamOutputDTO | null> {
return this.teamService.getDriverTeam(driverId);
const presenter = await this.teamService.getDriverTeam(driverId);
return presenter.getViewModel();
}
@Get(':teamId/members/:driverId')
@ApiOperation({ summary: 'Get team membership for a driver' })
@ApiResponse({ status: 200, description: 'Team membership', type: GetTeamMembershipOutputDTO })
@ApiResponse({ status: 404, description: 'Membership not found' })
async getMembership(@Param('teamId') teamId: string, @Param('driverId') driverId: string): Promise<GetTeamMembershipOutputDTO | null> {
return this.teamService.getMembership(teamId, driverId);
const presenter = await this.teamService.getMembership(teamId, driverId);
return presenter.viewModel;
}
}

View File

@@ -5,7 +5,6 @@ import { GetDriverTeamUseCase } from '@core/racing/application/use-cases/GetDriv
import type { Logger } from '@core/shared/application/Logger';
import { AllTeamsPresenter } from './presenters/AllTeamsPresenter';
import { DriverTeamPresenter } from './presenters/DriverTeamPresenter';
import { AllTeamsViewModel, DriverTeamViewModel } from './dtos/TeamDto';
describe('TeamService', () => {
let service: TeamService;

View File

@@ -1,14 +1,6 @@
import { Injectable, Inject } from '@nestjs/common';
import { GetAllTeamsOutputDTO } from './dtos/GetAllTeamsOutputDTO';
import { GetTeamDetailsOutputDTO } from './dtos/GetTeamDetailsOutputDTO';
import { GetTeamMembersOutputDTO } from './dtos/GetTeamMembersOutputDTO';
import { GetTeamJoinRequestsOutputDTO } from './dtos/GetTeamJoinRequestsOutputDTO';
import { CreateTeamInputDTO } from './dtos/CreateTeamInputDTO';
import { CreateTeamOutputDTO } from './dtos/CreateTeamOutputDTO';
import { UpdateTeamInputDTO } from './dtos/UpdateTeamInputDTO';
import { UpdateTeamOutputDTO } from './dtos/UpdateTeamOutputDTO';
import { GetDriverTeamOutputDTO } from './dtos/GetDriverTeamOutputDTO';
import { GetTeamMembershipOutputDTO } from './dtos/GetTeamMembershipOutputDTO';
// Core imports
import type { Logger } from '@core/shared/application/Logger';
@@ -29,6 +21,9 @@ 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 { LOGGER_TOKEN } from './TeamProviders';
@@ -47,125 +42,150 @@ export class TeamService {
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
) {}
async getAll(): Promise<GetAllTeamsOutputDTO> {
async getAll(): Promise<AllTeamsPresenter> {
this.logger.debug('[TeamService] Fetching all teams.');
const presenter = new AllTeamsPresenter();
const result = await this.getAllTeamsUseCase.execute();
if (result.isErr()) {
this.logger.error('Error fetching all teams', result.error);
return { teams: [], totalCount: 0 };
await presenter.present({ teams: [], totalCount: 0 });
return presenter;
}
await presenter.present(result.value);
return presenter.getViewModel()!;
return presenter;
}
async getDetails(teamId: string, userId?: string): Promise<GetTeamDetailsOutputDTO | null> {
async getDetails(teamId: string, userId?: string): Promise<TeamDetailsPresenter> {
this.logger.debug(`[TeamService] Fetching team details for teamId: ${teamId}, userId: ${userId}`);
const presenter = new TeamDetailsPresenter();
const result = await this.getTeamDetailsUseCase.execute({ teamId, driverId: userId || '' });
if (result.isErr()) {
this.logger.error(`Error fetching team details for teamId: ${teamId}`, result.error);
return null;
return presenter;
}
await presenter.present(result.value);
return presenter.getViewModel();
return presenter;
}
async getMembers(teamId: string): Promise<GetTeamMembersOutputDTO> {
async getMembers(teamId: string): Promise<TeamMembersPresenter> {
this.logger.debug(`[TeamService] Fetching team members for teamId: ${teamId}`);
const presenter = new TeamMembersPresenter();
const result = await this.getTeamMembersUseCase.execute({ teamId });
if (result.isErr()) {
this.logger.error(`Error fetching team members for teamId: ${teamId}`, result.error);
return {
await presenter.present({
members: [],
totalCount: 0,
ownerCount: 0,
managerCount: 0,
memberCount: 0,
};
} as unknown as any);
return presenter;
}
await presenter.present(result.value);
return presenter.getViewModel()!;
return presenter;
}
async getJoinRequests(teamId: string): Promise<GetTeamJoinRequestsOutputDTO> {
async getJoinRequests(teamId: string): Promise<TeamJoinRequestsPresenter> {
this.logger.debug(`[TeamService] Fetching team join requests for teamId: ${teamId}`);
const presenter = new TeamJoinRequestsPresenter();
const result = await this.getTeamJoinRequestsUseCase.execute({ teamId });
if (result.isErr()) {
this.logger.error(`Error fetching team join requests for teamId: ${teamId}`, result.error);
return {
await presenter.present({
requests: [],
pendingCount: 0,
totalCount: 0,
};
} as unknown as any);
return presenter;
}
await presenter.present(result.value);
return presenter.getViewModel()!;
return presenter;
}
async create(input: CreateTeamInputDTO, userId?: string): Promise<CreateTeamOutputDTO> {
async create(input: CreateTeamInputDTO, userId?: string): Promise<CreateTeamPresenter> {
this.logger.debug('[TeamService] Creating team', { input, userId });
const presenter = new CreateTeamPresenter();
const command = {
name: input.name,
tag: input.tag,
description: input.description,
description: input.description ?? '',
ownerId: userId || '',
leagues: [],
};
const result = await this.createTeamUseCase.execute(command);
const result = await this.createTeamUseCase.execute(command as any);
if (result.isErr()) {
this.logger.error('Error creating team', result.error);
return { id: '', success: false };
presenter.presentError();
return presenter;
}
return { id: result.value.id, success: true };
presenter.presentSuccess(result.value);
return presenter;
}
async update(teamId: string, input: UpdateTeamInputDTO, userId?: string): Promise<UpdateTeamOutputDTO> {
async update(teamId: string, input: UpdateTeamInputDTO, userId?: string): Promise<UpdateTeamPresenter> {
this.logger.debug(`[TeamService] Updating team ${teamId}`, { input, userId });
const presenter = new UpdateTeamPresenter();
const command = {
teamId,
name: input.name,
tag: input.tag,
description: input.description,
performerId: userId || '',
updates: {
name: input.name,
tag: input.tag,
description: input.description,
},
updatedBy: userId || '',
};
const result = await this.updateTeamUseCase.execute(command);
const result = await this.updateTeamUseCase.execute(command as any);
if (result.isErr()) {
this.logger.error(`Error updating team ${teamId}`, result.error);
return { success: false };
presenter.presentError();
return presenter;
}
return { success: true };
presenter.presentSuccess();
return presenter;
}
async getDriverTeam(driverId: string): Promise<GetDriverTeamOutputDTO | null> {
async getDriverTeam(driverId: string): Promise<DriverTeamPresenter> {
this.logger.debug(`[TeamService] Fetching driver team for driverId: ${driverId}`);
const presenter = new DriverTeamPresenter();
const result = await this.getDriverTeamUseCase.execute({ driverId });
if (result.isErr()) {
this.logger.error(`Error fetching driver team for driverId: ${driverId}`, result.error);
return null;
return presenter;
}
const presenter = new DriverTeamPresenter();
await presenter.present(result.value);
return presenter.getViewModel();
return presenter;
}
async getMembership(teamId: string, driverId: string): Promise<GetTeamMembershipOutputDTO | null> {
async getMembership(teamId: string, driverId: string): Promise<TeamMembershipPresenter> {
this.logger.debug(`[TeamService] Fetching team membership for teamId: ${teamId}, driverId: ${driverId}`);
const presenter = new TeamMembershipPresenter();
const result = await this.getTeamMembershipUseCase.execute({ teamId, driverId });
if (result.isErr()) {
this.logger.error(`Error fetching team membership for teamId: ${teamId}, driverId: ${driverId}`, result.error);
return null;
return presenter;
}
return result.value;
presenter.present(result.value as any);
return presenter;
}
}

View File

@@ -0,0 +1,36 @@
import type { CreateTeamOutputPort } from '@core/racing/application/ports/output/CreateTeamOutputPort';
import type { CreateTeamOutputDTO } from '../dtos/CreateTeamOutputDTO';
export class CreateTeamPresenter {
private result: CreateTeamOutputDTO | null = null;
reset(): void {
this.result = null;
}
presentSuccess(output: CreateTeamOutputPort): void {
this.result = {
id: output.team.id,
success: true,
};
}
presentError(): void {
this.result = {
id: '',
success: false,
};
}
getViewModel(): CreateTeamOutputDTO | null {
return this.result;
}
get viewModel(): CreateTeamOutputDTO {
if (!this.result) {
throw new Error('Presenter not presented');
}
return this.result;
}
}

View File

@@ -0,0 +1,30 @@
import type { GetTeamMembershipOutputDTO } from '../dtos/GetTeamMembershipOutputDTO';
export class TeamMembershipPresenter {
private result: GetTeamMembershipOutputDTO | null = null;
reset(): void {
this.result = null;
}
present(membership: GetTeamMembershipOutputDTO | null): void {
if (!membership) {
this.result = null;
return;
}
this.result = {
role: membership.role,
joinedAt: membership.joinedAt,
isActive: membership.isActive,
};
}
getViewModel(): GetTeamMembershipOutputDTO | null {
return this.result;
}
get viewModel(): GetTeamMembershipOutputDTO | null {
return this.result;
}
}

View File

@@ -0,0 +1,33 @@
import type { UpdateTeamOutputDTO } from '../dtos/UpdateTeamOutputDTO';
export class UpdateTeamPresenter {
private result: UpdateTeamOutputDTO | null = null;
reset(): void {
this.result = null;
}
presentSuccess(): void {
this.result = {
success: true,
};
}
presentError(): void {
this.result = {
success: false,
};
}
getViewModel(): UpdateTeamOutputDTO | null {
return this.result;
}
get viewModel(): UpdateTeamOutputDTO {
if (!this.result) {
throw new Error('Presenter not presented');
}
return this.result;
}
}