clean arch violations identified

This commit is contained in:
2026-01-08 00:03:10 +01:00
parent 606b64cec7
commit d984ab24a8
3 changed files with 766 additions and 20 deletions

View File

@@ -361,6 +361,144 @@ Inner layers:
This keeps the **automation engine** stable and reusable across presentation surfaces (companion today, web or backend orchestrators later), while allowing infrastructure and UI details to evolve without rewriting business logic. This keeps the **automation engine** stable and reusable across presentation surfaces (companion today, web or backend orchestrators later), while allowing infrastructure and UI details to evolve without rewriting business logic.
### 4.2 Critical Clean Architecture Principle: Use Cases Do NOT Call Presenters
**The most important rule in Clean Architecture is that use cases must remain completely independent of presentation concerns.**
#### ❌ WRONG PATTERN (What NOT to do)
```typescript
// ❌ VIOLATES CLEAN ARCHITECTURE
class GetRaceDetailUseCase {
constructor(
private repositories: any,
private output: UseCaseOutputPort<GetRaceDetailResult>
) {}
async execute(input: GetRaceDetailInput): Promise<Result<void, ApplicationError>> {
const race = await this.raceRepository.findById(input.raceId);
if (!race) {
// WRONG: Use case calling presenter
const result = Result.err({ code: 'RACE_NOT_FOUND', details: {...} });
this.output.present(result); // ❌ DON'T DO THIS
return result;
}
// WRONG: Use case calling presenter
const result = Result.ok({ race, league, registrations, drivers, userResult, isUserRegistered, canRegister });
this.output.present(result); // ❌ DON'T DO THIS
return result;
}
}
```
**Why this violates Clean Architecture:**
- Use cases now **know about presenters** and how to call them
- Creates **tight coupling** between application logic and presentation
- Makes use cases **untestable** without mocking presenters
- Violates the **Dependency Rule** (inner layer depending on outer layer behavior)
#### ✅ CORRECT PATTERN (Clean Architecture)
```typescript
// ✅ CLEAN ARCHITECTURE - Use case returns data, period
class GetRaceDetailUseCase {
constructor(
private repositories: any,
private output: UseCaseOutputPort<GetRaceDetailResult>
) {}
async execute(input: GetRaceDetailInput): Promise<Result<GetRaceDetailResult, ApplicationError>> {
const race = await this.raceRepository.findById(input.raceId);
if (!race) {
return Result.err({ code: 'RACE_NOT_FOUND', details: {...} });
// NO .present() call! Just returns the Result.
}
return Result.ok({ race, league, registrations, drivers, userResult, isUserRegistered, canRegister });
// NO .present() call! Just returns the Result.
}
}
```
**The Controller/Wiring Layer (Infrastructure/Presentation):**
```typescript
// ✅ Controller wires use case to presenter
class RaceController {
constructor(
private getRaceDetailUseCase: GetRaceDetailUseCase,
private raceDetailPresenter: RaceDetailPresenter
) {}
async getRaceDetail(params: GetRaceDetailParamsDTO): Promise<RaceDetailDTO> {
// 1. Execute use case
const result = await this.getRaceDetailUseCase.execute(params);
// 2. Pass result to presenter (wiring happens here)
this.raceDetailPresenter.present(result);
// 3. Get ViewModel from presenter
return this.raceDetailPresenter.viewModel;
}
}
```
### 4.3 The "Presenter Not Presented" Error Explained
Your current architecture has this error because:
1. **Use cases call `.present()`** (violating Clean Architecture)
2. **Controllers expect presenters to have `.viewModel`**
3. **But if use case returns early on error without calling `.present()`**, the presenter never gets data
4. **Controller tries to access `.viewModel`** → throws "Presenter not presented"
**The fix is NOT to add more `.present()` calls to use cases. The fix is to remove ALL `.present()` calls from use cases.**
### 4.4 Your Adapter Pattern is a Smokescreen
Your current code uses adapter classes like:
```typescript
class RaceDetailOutputAdapter implements UseCaseOutputPort<GetRaceDetailResult> {
constructor(private presenter: RaceDetailPresenter) {}
present(result: GetRaceDetailResult): void {
this.presenter.present(result);
}
}
```
**This is just hiding the crime.** The adapter still couples the use case to the presenter concept. The real Clean Architecture approach eliminates these adapters entirely and has controllers do the wiring.
### 4.5 The Real Clean Architecture Flow
```
1. Controller receives HTTP request
2. Controller calls UseCase.execute()
3. UseCase returns Result<T, E> (no presenter knowledge)
4. Controller passes Result to Presenter
5. Presenter transforms Result → ViewModel
6. Controller returns ViewModel to HTTP layer
```
**Key insight:** The use case's `output` port should be **the Result itself**, not a presenter. The controller is responsible for taking that Result and passing it to the appropriate presenter.
### 4.6 What This Means for Your Codebase
**To achieve 100% Clean Architecture, you must:**
1. **Remove all `.present()` calls from use cases** - they should only return Results
2. **Remove all adapter classes** - they're unnecessary coupling
3. **Make controllers wire use cases to presenters** - this is where the "glue" belongs
4. **Use cases return Results, period** - they don't know about presenters, viewmodels, or HTTP
**This is the ONLY way to achieve true Clean Architecture.** Any pattern where use cases call presenters is **not Clean Architecture**, regardless of how many adapter layers you add.
The "presenter not presented" error is a **symptom** of this architectural violation, not the root problem.
--- ---
## 4. Layer-by-Layer Mapping ## 4. Layer-by-Layer Mapping
@@ -717,11 +855,11 @@ This section describes how a typical hosted-session automation run flows through
- The automation engine (implemented by [`AutomationEngineAdapter`](core/infrastructure/adapters/automation/engine/AutomationEngineAdapter.ts:1) and backed by [`PlaywrightAutomationAdapter`](core/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1)) proceeds through steps: - The automation engine (implemented by [`AutomationEngineAdapter`](core/infrastructure/adapters/automation/engine/AutomationEngineAdapter.ts:1) and backed by [`PlaywrightAutomationAdapter`](core/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1)) proceeds through steps:
- Navigate to hosted sessions. - Navigate to hosted sessions.
- Open Create a Race and the hosted-session wizard. - Open "Create a Race" and the hosted-session wizard.
- For each step (race information, server details, admins, cars, tracks, weather, race options, conditions): - For each step (race information, server details, admins, cars, tracks, weather, race options, conditions):
- Ensure the correct page is active using [`PageStateValidator`](core/domain/services/PageStateValidator.ts:1) and selectors from [`IRACING_SELECTORS`](core/infrastructure/adapters/automation/dom/IRacingSelectors.ts:1). - Ensure the correct page is active using [`PageStateValidator`](core/domain/services/PageStateValidator.ts:1) and selectors from [`IRACING_SELECTORS`](core/infrastructure/adapters/automation/dom/IRacingSelectors.ts:1).
- Fill fields and toggles using [`IRacingDomInteractor`](core/infrastructure/adapters/automation/dom/IRacingDomInteractor.ts:1). - Fill fields and toggles using [`IRacingDomInteractor`](core/infrastructure/adapters/automation/dom/IRacingDomInteractor.ts:1).
- Click the correct Next / Create Race / New Race buttons, guarded by [`SafeClickService`](core/infrastructure/adapters/automation/dom/SafeClickService.ts:1) and blocked-selector logic. - Click the correct "Next" / "Create Race" / "New Race" buttons, guarded by [`SafeClickService`](core/infrastructure/adapters/automation/dom/SafeClickService.ts:1) and blocked-selector logic.
- At each step, the Playwright adapter: - At each step, the Playwright adapter:
- Updates the overlay via `updateOverlay(step, message)`. - Updates the overlay via `updateOverlay(step, message)`.
@@ -741,6 +879,60 @@ This section describes how a typical hosted-session automation run flows through
- The companion renderer may present a [`RaceCreationResult`](core/domain/value-objects/RaceCreationResult.ts:1) via [`RaceCreationSuccessScreen`](apps/companion/renderer/components/RaceCreationSuccessScreen.tsx). - The companion renderer may present a [`RaceCreationResult`](core/domain/value-objects/RaceCreationResult.ts:1) via [`RaceCreationSuccessScreen`](apps/companion/renderer/components/RaceCreationSuccessScreen.tsx).
- The browser context is closed or re-used based on mode and configuration; debug artifacts may be written by the Playwright adapter for failed runs. - The browser context is closed or re-used based on mode and configuration; debug artifacts may be written by the Playwright adapter for failed runs.
### 5.2 Clean Architecture Flow Example
**The correct Clean Architecture flow for use cases:**
```typescript
// ❌ WRONG - Current broken pattern
class GetRaceDetailUseCase {
async execute(input: GetRaceDetailInput): Promise<Result<void, ApplicationError>> {
const race = await this.raceRepository.findById(input.raceId);
if (!race) {
const result = Result.err({ code: 'RACE_NOT_FOUND', details: {...} });
this.output.present(result); // ❌ Use case calling presenter
return result;
}
const result = Result.ok({ race, league, registrations, drivers, userResult, isUserRegistered, canRegister });
this.output.present(result); // ❌ Use case calling presenter
return result;
}
}
// ✅ CORRECT - Clean Architecture
class GetRaceDetailUseCase {
async execute(input: GetRaceDetailInput): Promise<Result<GetRaceDetailResult, ApplicationError>> {
const race = await this.raceRepository.findById(input.raceId);
if (!race) {
return Result.err({ code: 'RACE_NOT_FOUND', details: {...} });
// ✅ No .present() call - just returns Result
}
return Result.ok({ race, league, registrations, drivers, userResult, isUserRegistered, canRegister });
// ✅ No .present() call - just returns Result
}
}
// Controller wiring (in infrastructure/presentation layer)
class RaceController {
async getRaceDetail(params: GetRaceDetailParamsDTO): Promise<RaceDetailDTO> {
// 1. Call use case
const result = await this.getRaceDetailUseCase.execute(params);
// 2. Wire to presenter
this.raceDetailPresenter.present(result);
// 3. Return ViewModel
return this.raceDetailPresenter.viewModel;
}
}
```
**This is the ONLY pattern that respects Clean Architecture.** Your current architecture violates this fundamental principle, which is why you have the "presenter not presented" problem.
--- ---
## 6. How This Serves Admins, Drivers, Teams ## 6. How This Serves Admins, Drivers, Teams

View File

@@ -5,13 +5,13 @@ according to Clean Architecture, in a NestJS-based system.
The goal is: The goal is:
• strict separation of concerns • strict separation of concerns
• correct terminology (no fake ports) • correct terminology (no fake "ports")
• minimal abstractions • minimal abstractions
• long-term consistency • long-term consistency
This is the canonical reference for all use cases in this codebase. This is the canonical reference for all use cases in this codebase.
1. Core Concepts (Authoritative Definitions) 1. Core Concepts (Authoritative Definitions)
@@ -24,7 +24,7 @@ Use Case
The public execute() method is the input port. The public execute() method is the input port.
Input Input
• Pure data • Pure data
@@ -37,7 +37,7 @@ type GetSponsorsInput = {
} }
Result Result
• The business outcome of a use case • The business outcome of a use case
@@ -50,7 +50,7 @@ type GetSponsorsResult = {
} }
Output Port Output Port
• A behavioral boundary • A behavioral boundary
@@ -63,7 +63,7 @@ export interface UseCaseOutputPort<T> {
} }
Presenter Presenter
• Implements UseCaseOutputPort<T> • Implements UseCaseOutputPort<T>
@@ -72,7 +72,7 @@ Presenter
• Holds internal state • Holds internal state
• Is pulled by the controller after execution • Is pulled by the controller after execution
2. Canonical Use Case Structure 2. Canonical Use Case Structure
@@ -102,7 +102,85 @@ Rules:
• All output flows through the OutputPort • All output flows through the OutputPort
• The return value signals success or failure only • The return value signals success or failure only
### ⚠️ ARCHITECTURAL VIOLATION ALERT
**The pattern shown above is INCORRECT and violates Clean Architecture.**
#### ❌ WRONG PATTERN (What NOT to do)
```typescript
@Injectable()
export class GetSponsorsUseCase {
constructor(
private readonly sponsorRepository: ISponsorRepository,
private readonly output: UseCaseOutputPort<GetSponsorsResult>,
) {}
async execute(): Promise<Result<void, ApplicationError>> {
const sponsors = await this.sponsorRepository.findAll()
this.output.present({ sponsors }) // ❌ WRONG: Use case calling presenter
return Result.ok(undefined)
}
}
```
**Why this violates Clean Architecture:**
- Use cases **know about presenters** and how to call them
- Creates **tight coupling** between application logic and presentation
- Makes use cases **untestable** without mocking presenters
- Violates the **Dependency Rule** (inner layer depending on outer layer behavior)
#### ✅ CORRECT PATTERN (Clean Architecture)
```typescript
@Injectable()
export class GetSponsorsUseCase {
constructor(
private readonly sponsorRepository: ISponsorRepository,
// NO output port needed in constructor
) {}
async execute(): Promise<Result<GetSponsorsResult, ApplicationError>> {
const sponsors = await this.sponsorRepository.findAll()
return Result.ok({ sponsors })
// ✅ Returns Result, period. No .present() call.
}
}
```
**The Controller (in API layer) handles the wiring:**
```typescript
@Controller('/sponsors')
export class SponsorsController {
constructor(
private readonly useCase: GetSponsorsUseCase,
private readonly presenter: GetSponsorsPresenter,
) {}
@Get()
async getSponsors() {
// 1. Execute use case
const result = await this.useCase.execute()
if (result.isErr()) {
throw mapApplicationError(result.unwrapErr())
}
// 2. Wire to presenter
this.presenter.present(result.value)
// 3. Return ViewModel
return this.presenter.getViewModel()
}
}
```
**This is the ONLY pattern that respects Clean Architecture.**
Result Model Result Model
@@ -116,7 +194,7 @@ Rules:
• No interfaces • No interfaces
• No transport concerns • No transport concerns
3. API Layer 3. API Layer
@@ -158,7 +236,7 @@ export class GetSponsorsPresenter
} }
Controller Controller
@@ -182,7 +260,7 @@ export class SponsorsController {
} }
Payments Example Payments Example
@@ -266,7 +344,7 @@ export class PaymentsController {
} }
} }
4. Module Wiring (Composition Root) 4. Module Wiring (Composition Root)
@@ -287,7 +365,7 @@ Rules:
• The presenter is bound as the OutputPort implementation • The presenter is bound as the OutputPort implementation
• process.env is not used inside the use case • process.env is not used inside the use case
5. Explicitly Forbidden 5. Explicitly Forbidden
@@ -299,7 +377,7 @@ Rules:
❌ Mapping logic inside use cases ❌ Mapping logic inside use cases
❌ Environment access inside the core ❌ Environment access inside the core
Do / Dont (Boundary Examples) Do / Dont (Boundary Examples)
@@ -307,6 +385,8 @@ Do / Dont (Boundary Examples)
✅ DO: Keep controllers/services thin and delegating, e.g. [LeagueController.createLeagueSeasonScheduleRace()](apps/api/src/domain/league/LeagueController.ts:291). ✅ DO: Keep controllers/services thin and delegating, e.g. [LeagueController.createLeagueSeasonScheduleRace()](apps/api/src/domain/league/LeagueController.ts:291).
❌ DONT: Put business rules in the API layer; rules belong in `./core` use cases/entities/value objects, e.g. [CreateLeagueSeasonScheduleRaceUseCase.execute()](core/racing/application/use-cases/CreateLeagueSeasonScheduleRaceUseCase.ts:38). ❌ DONT: Put business rules in the API layer; rules belong in `./core` use cases/entities/value objects, e.g. [CreateLeagueSeasonScheduleRaceUseCase.execute()](core/racing/application/use-cases/CreateLeagueSeasonScheduleRaceUseCase.ts:38).
6. Optional Extensions 6. Optional Extensions
Custom Output Ports Custom Output Ports
@@ -322,7 +402,7 @@ interface ComplexOutputPort {
} }
Input Port Interfaces Input Port Interfaces
@@ -335,7 +415,7 @@ Otherwise:
The use case class itself is the input port. The use case class itself is the input port.
7. Key Rules (Memorize These) 7. Key Rules (Memorize These)
@@ -348,7 +428,7 @@ Data does not.
The core produces truth. The core produces truth.
The API interprets it. The API interprets it.
TL;DR TL;DR
• Use cases are injected via DI • Use cases are injected via DI
@@ -357,4 +437,77 @@ TL;DR
• Results are business models, not DTOs • Results are business models, not DTOs
• Interfaces exist only for behavior variability • Interfaces exist only for behavior variability
This document is the single source of truth for use case architecture in this project. ### 🚨 CRITICAL CLEAN ARCHITECTURE CORRECTION
**The examples in this document (sections 2, 3, and the Payments Example) demonstrate the WRONG pattern that violates Clean Architecture.**
#### The Fundamental Problem
The current architecture shows use cases **calling presenters directly**:
```typescript
// ❌ WRONG - This violates Clean Architecture
this.output.present({ sponsors })
```
**This is architecturally incorrect.** Use cases must **never** know about presenters or call `.present()`.
#### The Correct Clean Architecture Pattern
**Use cases return Results. Controllers wire them to presenters.**
```typescript
// ✅ CORRECT - Use case returns data
@Injectable()
export class GetSponsorsUseCase {
constructor(private readonly sponsorRepository: ISponsorRepository) {}
async execute(): Promise<Result<GetSponsorsResult, ApplicationError>> {
const sponsors = await this.sponsorRepository.findAll()
return Result.ok({ sponsors })
// NO .present() call!
}
}
// ✅ CORRECT - Controller handles wiring
@Controller('/sponsors')
export class SponsorsController {
constructor(
private readonly useCase: GetSponsorsUseCase,
private readonly presenter: GetSponsorsPresenter,
) {}
@Get()
async getSponsors() {
const result = await this.useCase.execute()
if (result.isErr()) {
throw mapApplicationError(result.unwrapErr())
}
this.presenter.present(result.value)
return this.presenter.getViewModel()
}
}
```
#### Why This Matters
1. **Dependency Rule**: Inner layers (use cases) cannot depend on outer layers (presenters)
2. **Testability**: Use cases can be tested without mocking presenters
3. **Flexibility**: Same use case can work with different presenters
4. **Separation of Concerns**: Use cases do business logic, presenters do transformation
#### What Must Be Fixed
**All use cases in the codebase must be updated to:**
1. **Remove** the `output: UseCaseOutputPort<T>` constructor parameter
2. **Return** `Result<T, E>` directly from `execute()`
3. **Remove** all `this.output.present()` calls
**All controllers must be updated to:**
1. **Call** the use case and get the Result
2. **Pass** `result.value` to the presenter's `.present()` method
3. **Return** the presenter's `.getViewModel()`
This is the **single source of truth** for correct Clean Architecture in this project.

View File

@@ -0,0 +1,401 @@
# 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<GetRaceDetailResult> // ❌ Wrong
) {}
async execute(input: GetRaceDetailInput): Promise<Result<void, ApplicationError>> {
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<Result<GetRaceDetailResult, ApplicationError>> {
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<RaceDetailPresenter> {
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<GetRaceDetailResult>` from constructor
- Change return type from `Promise<Result<void, ApplicationError>>` to `Promise<Result<GetRaceDetailResult, ApplicationError>>`
- 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<T>` from constructor
- [ ] Change return type from `Promise<Result<void, E>>` to `Promise<Result<T, E>>`
- [ ] 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<T, E>` 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.**