Files
puaros/packages/guardian/src/infrastructure/analyzers/RepositoryPatternDetector.ts
imfozilbek 8d400c9517 refactor: extract detector logic into focused strategy classes
Refactored three largest detectors to improve maintainability and reduce complexity:

- AggregateBoundaryDetector: 381 → 162 lines (57% reduction)
- HardcodeDetector: 459 → 89 lines (81% reduction)
- RepositoryPatternDetector: 479 → 106 lines (78% reduction)

Added 13 new strategy classes:
- FolderRegistry - centralized DDD folder name management
- AggregatePathAnalyzer - path parsing and aggregate extraction
- ImportValidator - import validation logic
- BraceTracker - brace and bracket counting
- ConstantsFileChecker - constants file detection
- ExportConstantAnalyzer - export const analysis
- MagicNumberMatcher - magic number detection
- MagicStringMatcher - magic string detection
- OrmTypeMatcher - ORM type matching
- MethodNameValidator - repository method validation
- RepositoryFileAnalyzer - file role detection
- RepositoryViolationDetector - violation detection logic

All 519 tests passing, zero ESLint errors, no breaking changes.
2025-11-25 17:41:32 +05:00

107 lines
3.8 KiB
TypeScript

import { IRepositoryPatternDetector } from "../../domain/services/RepositoryPatternDetectorService"
import { RepositoryViolation } from "../../domain/value-objects/RepositoryViolation"
import { OrmTypeMatcher } from "../strategies/OrmTypeMatcher"
import { MethodNameValidator } from "../strategies/MethodNameValidator"
import { RepositoryFileAnalyzer } from "../strategies/RepositoryFileAnalyzer"
import { RepositoryViolationDetector } from "../strategies/RepositoryViolationDetector"
/**
* Detects Repository Pattern violations in the codebase
*
* This detector identifies violations where the Repository Pattern is not properly implemented:
* 1. ORM-specific types in repository interfaces (domain should be ORM-agnostic)
* 2. Concrete repository usage in use cases (violates dependency inversion)
* 3. Repository instantiation with 'new' in use cases (should use DI)
* 4. Non-domain method names in repositories (should use ubiquitous language)
*
* @example
* ```typescript
* const detector = new RepositoryPatternDetector()
*
* // Detect violations in a repository interface
* const code = `
* interface IUserRepository {
* findOne(query: Prisma.UserWhereInput): Promise<User>
* }
* `
* const violations = detector.detectViolations(
* code,
* 'src/domain/repositories/IUserRepository.ts',
* 'domain'
* )
*
* // violations will contain ORM type violation
* console.log(violations.length) // 1
* console.log(violations[0].violationType) // 'orm-type-in-interface'
* ```
*/
export class RepositoryPatternDetector implements IRepositoryPatternDetector {
private readonly ormMatcher: OrmTypeMatcher
private readonly methodValidator: MethodNameValidator
private readonly fileAnalyzer: RepositoryFileAnalyzer
private readonly violationDetector: RepositoryViolationDetector
constructor() {
this.ormMatcher = new OrmTypeMatcher()
this.methodValidator = new MethodNameValidator(this.ormMatcher)
this.fileAnalyzer = new RepositoryFileAnalyzer()
this.violationDetector = new RepositoryViolationDetector(
this.ormMatcher,
this.methodValidator,
)
}
/**
* Detects all Repository Pattern violations in the given code
*/
public detectViolations(
code: string,
filePath: string,
layer: string | undefined,
): RepositoryViolation[] {
const violations: RepositoryViolation[] = []
if (this.fileAnalyzer.isRepositoryInterface(filePath, layer)) {
violations.push(...this.violationDetector.detectOrmTypes(code, filePath, layer))
violations.push(...this.violationDetector.detectNonDomainMethods(code, filePath, layer))
}
if (this.fileAnalyzer.isUseCase(filePath, layer)) {
violations.push(
...this.violationDetector.detectConcreteRepositoryUsage(code, filePath, layer),
)
violations.push(...this.violationDetector.detectNewInstantiation(code, filePath, layer))
}
return violations
}
/**
* Checks if a type is an ORM-specific type
*/
public isOrmType(typeName: string): boolean {
return this.ormMatcher.isOrmType(typeName)
}
/**
* Checks if a method name follows domain language conventions
*/
public isDomainMethodName(methodName: string): boolean {
return this.methodValidator.isDomainMethodName(methodName)
}
/**
* Checks if a file is a repository interface
*/
public isRepositoryInterface(filePath: string, layer: string | undefined): boolean {
return this.fileAnalyzer.isRepositoryInterface(filePath, layer)
}
/**
* Checks if a file is a use case
*/
public isUseCase(filePath: string, layer: string | undefined): boolean {
return this.fileAnalyzer.isUseCase(filePath, layer)
}
}