# Clean Architecture Violation Fix Plan ## Executive Summary **Problem**: The codebase violates Clean Architecture by having use cases call presenters directly (`this.output.present()`), creating tight coupling and causing "Presenter not presented" errors. **Root Cause**: Use cases are doing the presenter's job instead of returning data and letting controllers handle the wiring. **Solution**: Remove ALL `.present()` calls from use cases. Use cases return Results. Controllers wire Results to Presenters. --- ## The Violation Pattern ### ❌ Current Wrong Pattern (Violates Clean Architecture) ```typescript // core/racing/application/use-cases/GetRaceDetailUseCase.ts class GetRaceDetailUseCase { constructor( private repositories: any, private output: UseCaseOutputPort // ❌ Wrong ) {} async execute(input: GetRaceDetailInput): Promise> { const race = await this.raceRepository.findById(input.raceId); if (!race) { const result = Result.err({ code: 'RACE_NOT_FOUND', details: {...} }); this.output.present(result); // ❌ WRONG: Use case calling presenter return result; } const result = Result.ok({ race, league, registrations, drivers, userResult, isUserRegistered, canRegister }); this.output.present(result); // ❌ WRONG: Use case calling presenter return result; } } ``` ### ✅ Correct Pattern (Clean Architecture) ```typescript // core/racing/application/use-cases/GetRaceDetailUseCase.ts class GetRaceDetailUseCase { constructor( private repositories: any, // NO output port - removed ) {} async execute(input: GetRaceDetailInput): Promise> { const race = await this.raceRepository.findById(input.raceId); if (!race) { return Result.err({ code: 'RACE_NOT_FOUND', details: {...} }); // ✅ No .present() call } return Result.ok({ race, league, registrations, drivers, userResult, isUserRegistered, canRegister }); // ✅ No .present() call } } // apps/api/src/domain/race/RaceService.ts (Controller layer) class RaceService { constructor( private getRaceDetailUseCase: GetRaceDetailUseCase, private raceDetailPresenter: RaceDetailPresenter, ) {} async getRaceDetail(params: GetRaceDetailParamsDTO): Promise { const result = await this.getRaceDetailUseCase.execute(params); if (result.isErr()) { throw new NotFoundException(result.error.details.message); } this.raceDetailPresenter.present(result.value); // ✅ Controller wires to presenter return this.raceDetailPresenter; } } ``` --- ## What Needs To Be Done ### Phase 1: Fix Use Cases (Remove Output Ports) **Files to modify in `core/racing/application/use-cases/`:** 1. **GetRaceDetailUseCase.ts** (lines 35-44, 46-115) - Remove `output: UseCaseOutputPort` from constructor - Change return type from `Promise>` to `Promise>` - Remove all `this.output.present()` calls (lines 100, 109-112) 2. **GetRaceRegistrationsUseCase.ts** (lines 27-29, 31-70) - Remove output port from constructor - Change return type - Remove `this.output.present()` calls (lines 40-43, 66-69) 3. **GetLeagueFullConfigUseCase.ts** (lines 35-37, 39-92) - Remove output port from constructor - Change return type - Remove `this.output.present()` calls (lines 47-50, 88-91) 4. **GetRaceWithSOFUseCase.ts** (lines 43-45, 47-118) - Remove output port from constructor - Change return type - Remove `this.output.present()` calls (lines 58-61, 114-117) 5. **GetRaceResultsDetailUseCase.ts** (lines 41-43, 45-100) - Remove output port from constructor - Change return type - Remove `this.output.present()` calls (lines 56-59, 95-98) **Continue this pattern for ALL 150+ use cases listed in your original analysis.** ### Phase 2: Fix Controllers/Services (Add Wiring Logic) **Files to modify in `apps/api/src/domain/`:** 1. **RaceService.ts** (lines 135-139) - Update `getRaceDetail()` to wire use case result to presenter - Add error handling for Result.Err cases 2. **RaceProviders.ts** (lines 138-144, 407-437) - Remove adapter classes that wrap presenters - Update provider factories to inject presenters directly to controllers - Remove `RaceDetailOutputAdapter` and similar classes 3. **All other service files** that use use cases - Update method signatures to handle Results - Add proper error mapping - Wire results to presenters ### Phase 3: Update Module Wiring **Files to modify:** 1. **RaceProviders.ts** (lines 287-779) - Remove all adapter classes (lines 111-285) - Update provider definitions to not use adapters - Simplify dependency injection 2. **All other provider files** in `apps/api/src/domain/*/` - Remove adapter patterns - Update DI containers ### Phase 4: Fix Presenters (If Needed) **Some presenters may need updates:** 1. **RaceDetailPresenter.ts** (lines 15-26, 28-114) - Ensure `present()` method accepts `GetRaceDetailResult` directly - No changes needed if already correct 2. **CommandResultPresenter.ts** and similar - Ensure they work with Results from controllers, not use cases --- ## Implementation Checklist ### For Each Use Case File: - [ ] Remove `output: UseCaseOutputPort` from constructor - [ ] Change return type from `Promise>` to `Promise>` - [ ] Remove all `this.output.present()` calls - [ ] Return Result directly - [ ] Update imports if needed ### For Each Controller/Service File: - [ ] Update methods to call use case and get Result - [ ] Add `if (result.isErr())` error handling - [ ] Call `presenter.present(result.value)` after success - [ ] Return presenter or ViewModel - [ ] Remove adapter usage ### For Each Provider File: - [ ] Remove adapter classes - [ ] Update DI to inject presenters to controllers - [ ] Simplify provider definitions --- ## Files That Need Immediate Attention ### High Priority (Core Racing Domain): ``` core/racing/application/use-cases/GetRaceDetailUseCase.ts core/racing/application/use-cases/GetRaceRegistrationsUseCase.ts core/racing/application/use-cases/GetLeagueFullConfigUseCase.ts core/racing/application/use-cases/GetRaceWithSOFUseCase.ts core/racing/application/use-cases/GetRaceResultsDetailUseCase.ts core/racing/application/use-cases/GetLeagueAdminPermissionsUseCase.ts core/racing/application/use-cases/CompleteRaceUseCase.ts core/racing/application/use-cases/ApplyPenaltyUseCase.ts core/racing/application/use-cases/JoinLeagueUseCase.ts core/racing/application/use-cases/JoinTeamUseCase.ts core/racing/application/use-cases/RegisterForRaceUseCase.ts core/racing/application/use-cases/WithdrawFromRaceUseCase.ts core/racing/application/use-cases/CancelRaceUseCase.ts core/racing/application/use-cases/ReopenRaceUseCase.ts core/racing/application/use-cases/CompleteRaceUseCaseWithRatings.ts core/racing/application/use-cases/ImportRaceResultsUseCase.ts core/racing/application/use-cases/ImportRaceResultsApiUseCase.ts core/racing/application/use-cases/FileProtestUseCase.ts core/racing/application/use-cases/ReviewProtestUseCase.ts core/racing/application/use-cases/QuickPenaltyUseCase.ts core/racing/application/use-cases/ApplyForSponsorshipUseCase.ts core/racing/application/use-cases/AcceptSponsorshipRequestUseCase.ts core/racing/application/use-cases/RejectSponsorshipRequestUseCase.ts core/racing/application/use-cases/GetSponsorDashboardUseCase.ts core/racing/application/use-cases/GetSponsorSponsorshipsUseCase.ts core/racing/application/use-cases/GetSponsorshipPricingUseCase.ts core/racing/application/use-cases/GetEntitySponsorshipPricingUseCase.ts core/racing/application/use-cases/GetSeasonSponsorshipsUseCase.ts core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase.ts core/racing/application/use-cases/GetLeagueWalletUseCase.ts core/racing/application/use-cases/WithdrawFromLeagueWalletUseCase.ts core/racing/application/use-cases/GetLeagueStatsUseCase.ts core/racing/application/use-cases/GetLeagueMembershipsUseCase.ts core/racing/application/use-cases/GetLeagueJoinRequestsUseCase.ts core/racing/application/use-cases/GetTeamJoinRequestsUseCase.ts core/racing/application/use-cases/GetLeagueRosterMembersUseCase.ts core/racing/application/use-cases/GetLeagueRosterJoinRequestsUseCase.ts core/racing/application/use-cases/ApproveLeagueJoinRequestUseCase.ts core/racing/application/use-cases/RejectLeagueJoinRequestUseCase.ts core/racing/application/use-cases/UpdateLeagueMemberRoleUseCase.ts core/racing/application/use-cases/RemoveLeagueMemberUseCase.ts core/racing/application/use-cases/TransferLeagueOwnershipUseCase.ts core/racing/application/use-cases/GetLeagueAdminUseCase.ts core/racing/application/use-cases/GetLeagueOwnerSummaryUseCase.ts core/racing/application/use-cases/GetLeagueScoringConfigUseCase.ts core/racing/application/use-cases/ListLeagueScoringPresetsUseCase.ts core/racing/application/use-cases/GetLeagueScheduleUseCase.ts core/racing/application/use-cases/CreateLeagueSeasonScheduleRaceUseCase.ts core/racing/application/use-cases/UpdateLeagueSeasonScheduleRaceUseCase.ts core/racing/application/use-cases/DeleteLeagueSeasonScheduleRaceUseCase.ts core/racing/application/use-cases/PublishLeagueSeasonScheduleUseCase.ts core/racing/application/use-cases/UnpublishLeagueSeasonScheduleUseCase.ts core/racing/application/use-cases/PreviewLeagueScheduleUseCase.ts core/racing/application/use-cases/GetSeasonDetailsUseCase.ts core/racing/application/use-cases/ListSeasonsForLeagueUseCase.ts core/racing/application/use-cases/CreateSeasonForLeagueUseCase.ts core/racing/application/use-cases/CreateLeagueWithSeasonAndScoringUseCase.ts core/racing/application/use-cases/ManageSeasonLifecycleUseCase.ts core/racing/application/use-cases/RecalculateChampionshipStandingsUseCase.ts core/racing/application/use-cases/GetLeagueStandingsUseCase.ts core/racing/application/use-cases/GetDriversLeaderboardUseCase.ts core/racing/application/use-cases/GetTeamsLeaderboardUseCase.ts core/racing/application/use-cases/GetTotalDriversUseCase.ts core/racing/application/use-cases/GetTotalLeaguesUseCase.ts core/racing/application/use-cases/GetTotalRacesUseCase.ts core/racing/application/use-cases/GetAllRacesUseCase.ts core/racing/application/use-cases/GetAllRacesPageDataUseCase.ts core/racing/application/use-cases/GetRacesPageDataUseCase.ts core/racing/application/use-cases/GetAllLeaguesWithCapacityAndScoringUseCase.ts core/racing/application/use-cases/GetAllTeamsUseCase.ts core/racing/application/use-cases/GetTeamDetailsUseCase.ts core/racing/application/use-cases/GetTeamMembersUseCase.ts core/racing/application/use-cases/UpdateTeamUseCase.ts core/racing/application/use-cases/CreateTeamUseCase.ts core/racing/application/use-cases/LeaveTeamUseCase.ts core/racing/application/use-cases/ApproveTeamJoinRequestUseCase.ts core/racing/application/use-cases/RejectTeamJoinRequestUseCase.ts core/racing/application/use-cases/GetDriverTeamUseCase.ts core/racing/application/use-cases/GetProfileOverviewUseCase.ts core/racing/application/use-cases/CompleteDriverOnboardingUseCase.ts core/racing/application/use-cases/UpdateDriverProfileUseCase.ts core/racing/application/use-cases/SendFinalResultsUseCase.ts core/racing/application/use-cases/SendPerformanceSummaryUseCase.ts core/racing/application/use-cases/RequestProtestDefenseUseCase.ts core/racing/application/use-cases/SubmitProtestDefenseUseCase.ts core/racing/application/use-cases/GetRaceProtestsUseCase.ts core/racing/application/use-cases/GetLeagueProtestsUseCase.ts core/racing/application/use-cases/GetRacePenaltiesUseCase.ts core/racing/application/use-cases/GetSponsorsUseCase.ts core/racing/application/use-cases/GetSponsorUseCase.ts core/racing/application/use-cases/CreateSponsorUseCase.ts core/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase.ts core/racing/application/use-cases/GetLeagueStatsUseCase.ts ``` ### Medium Priority (Media Domain): ``` core/media/application/use-cases/GetAvatarUseCase.ts core/media/application/use-cases/GetMediaUseCase.ts core/media/application/use-cases/DeleteMediaUseCase.ts core/media/application/use-cases/UploadMediaUseCase.ts core/media/application/use-cases/UpdateAvatarUseCase.ts core/media/application/use-cases/RequestAvatarGenerationUseCase.ts core/media/application/use-cases/SelectAvatarUseCase.ts ``` ### Medium Priority (Identity Domain): ``` core/identity/application/use-cases/SignupUseCase.ts core/identity/application/use-cases/SignupWithEmailUseCase.ts core/identity/application/use-cases/LoginUseCase.ts core/identity/application/use-cases/LoginWithEmailUseCase.ts core/identity/application/use-cases/ForgotPasswordUseCase.ts core/identity/application/use-cases/ResetPasswordUseCase.ts core/identity/application/use-cases/GetCurrentSessionUseCase.ts core/identity/application/use-cases/GetCurrentUserSessionUseCase.ts core/identity/application/use-cases/LogoutUseCase.ts core/identity/application/use-cases/StartAuthUseCase.ts core/identity/application/use-cases/HandleAuthCallbackUseCase.ts core/identity/application/use-cases/SignupSponsorUseCase.ts core/identity/application/use-cases/CreateAchievementUseCase.ts ``` ### Medium Priority (Notifications Domain): ``` core/notifications/application/use-cases/GetUnreadNotificationsUseCase.ts core/notifications/application/use-cases/MarkNotificationReadUseCase.ts core/notifications/application/use-cases/NotificationPreferencesUseCases.ts core/notifications/application/use-cases/SendNotificationUseCase.ts ``` ### Medium Priority (Analytics Domain): ``` core/analytics/application/use-cases/GetAnalyticsMetricsUseCase.ts core/analytics/application/use-cases/GetDashboardDataUseCase.ts core/analytics/application/use-cases/RecordPageViewUseCase.ts core/analytics/application/use-cases/RecordEngagementUseCase.ts ``` ### Medium Priority (Admin Domain): ``` core/admin/application/use-cases/ListUsersUseCase.ts ``` ### Medium Priority (Social Domain): ``` core/social/application/use-cases/GetUserFeedUseCase.ts core/social/application/use-cases/GetCurrentUserSocialUseCase.ts ``` ### Medium Priority (Payments Domain): ``` core/payments/application/use-cases/GetWalletUseCase.ts core/payments/application/use-cases/GetMembershipFeesUseCase.ts core/payments/application/use-cases/UpdatePaymentStatusUseCase.ts core/payments/application/use-cases/AwardPrizeUseCase.ts core/payments/application/use-cases/DeletePrizeUseCase.ts core/payments/application/use-cases/CreatePrizeUseCase.ts core/payments/application/use-cases/CreatePaymentUseCase.ts core/payments/application/use-cases/ProcessWalletTransactionUseCase.ts core/payments/application/use-cases/UpdateMemberPaymentUseCase.ts core/payments/application/use-cases/GetPaymentsUseCase.ts core/payments/application/use-cases/UpsertMembershipFeeUseCase.ts ``` ### Controller/Service Files: ``` apps/api/src/domain/race/RaceService.ts apps/api/src/domain/race/RaceProviders.ts apps/api/src/domain/sponsor/SponsorService.ts apps/api/src/domain/league/LeagueService.ts apps/api/src/domain/driver/DriverService.ts apps/api/src/domain/auth/AuthService.ts apps/api/src/domain/analytics/AnalyticsService.ts apps/api/src/domain/notifications/NotificationsService.ts apps/api/src/domain/payments/PaymentsService.ts apps/api/src/domain/admin/AdminService.ts apps/api/src/domain/social/SocialService.ts apps/api/src/domain/media/MediaService.ts ``` --- ## Success Criteria ✅ **All use cases return `Result` directly** ✅ **No use case calls `.present()`** ✅ **All controllers wire Results to Presenters** ✅ **All adapter classes removed** ✅ **Module wiring simplified** ✅ **"Presenter not presented" errors eliminated** ✅ **Tests updated and passing** --- ## Estimated Effort - **150+ use cases** to fix - **20+ controller/service files** to update - **10+ provider files** to simplify - **Estimated time**: 2-3 days of focused work - **Risk**: Medium (requires careful testing) --- ## Next Steps 1. **Start with Phase 1**: Fix the core racing use cases first (highest impact) 2. **Test each change**: Run existing tests to ensure no regressions 3. **Update controllers**: Wire Results to Presenters 4. **Simplify providers**: Remove adapter classes 5. **Run full test suite**: Verify everything works **This plan provides the roadmap to achieve 100% Clean Architecture compliance.**