Files
puaros/packages/guardian/src/infrastructure/strategies/MethodNameValidator.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

135 lines
4.0 KiB
TypeScript

import { REPOSITORY_METHOD_SUGGESTIONS } from "../constants/detectorPatterns"
import { OrmTypeMatcher } from "./OrmTypeMatcher"
/**
* Validates repository method names for domain language compliance
*
* Ensures repository methods use domain language instead of
* technical database terminology.
*/
export class MethodNameValidator {
private readonly domainMethodPatterns = [
/^findBy[A-Z]/,
/^findAll$/,
/^find[A-Z]/,
/^save$/,
/^saveAll$/,
/^create$/,
/^update$/,
/^delete$/,
/^deleteBy[A-Z]/,
/^deleteAll$/,
/^remove$/,
/^removeBy[A-Z]/,
/^removeAll$/,
/^add$/,
/^add[A-Z]/,
/^get[A-Z]/,
/^getAll$/,
/^search/,
/^list/,
/^has[A-Z]/,
/^is[A-Z]/,
/^exists$/,
/^exists[A-Z]/,
/^existsBy[A-Z]/,
/^clear[A-Z]/,
/^clearAll$/,
/^store[A-Z]/,
/^initialize$/,
/^initializeCollection$/,
/^close$/,
/^connect$/,
/^disconnect$/,
/^count$/,
/^countBy[A-Z]/,
]
constructor(private readonly ormMatcher: OrmTypeMatcher) {}
/**
* Checks if a method name follows domain language conventions
*/
public isDomainMethodName(methodName: string): boolean {
if (this.ormMatcher.isTechnicalMethod(methodName)) {
return false
}
return this.domainMethodPatterns.some((pattern) => pattern.test(methodName))
}
/**
* Suggests better domain method names
*/
public suggestDomainMethodName(methodName: string): string {
const lowerName = methodName.toLowerCase()
const suggestions: string[] = []
this.collectSuggestions(lowerName, suggestions)
if (lowerName.includes("get") && lowerName.includes("all")) {
suggestions.push(
REPOSITORY_METHOD_SUGGESTIONS.FIND_ALL,
REPOSITORY_METHOD_SUGGESTIONS.LIST_ALL,
)
}
if (suggestions.length === 0) {
return REPOSITORY_METHOD_SUGGESTIONS.DEFAULT_SUGGESTION
}
return `Consider: ${suggestions.slice(0, 3).join(", ")}`
}
/**
* Collects method name suggestions based on keywords
*/
private collectSuggestions(lowerName: string, suggestions: string[]): void {
const suggestionMap: Record<string, string[]> = {
query: [
REPOSITORY_METHOD_SUGGESTIONS.SEARCH,
REPOSITORY_METHOD_SUGGESTIONS.FIND_BY_PROPERTY,
],
select: [
REPOSITORY_METHOD_SUGGESTIONS.FIND_BY_PROPERTY,
REPOSITORY_METHOD_SUGGESTIONS.GET_ENTITY,
],
insert: [
REPOSITORY_METHOD_SUGGESTIONS.CREATE,
REPOSITORY_METHOD_SUGGESTIONS.ADD_ENTITY,
REPOSITORY_METHOD_SUGGESTIONS.STORE_ENTITY,
],
update: [
REPOSITORY_METHOD_SUGGESTIONS.UPDATE,
REPOSITORY_METHOD_SUGGESTIONS.MODIFY_ENTITY,
],
upsert: [
REPOSITORY_METHOD_SUGGESTIONS.SAVE,
REPOSITORY_METHOD_SUGGESTIONS.STORE_ENTITY,
],
remove: [
REPOSITORY_METHOD_SUGGESTIONS.DELETE,
REPOSITORY_METHOD_SUGGESTIONS.REMOVE_BY_PROPERTY,
],
fetch: [
REPOSITORY_METHOD_SUGGESTIONS.FIND_BY_PROPERTY,
REPOSITORY_METHOD_SUGGESTIONS.GET_ENTITY,
],
retrieve: [
REPOSITORY_METHOD_SUGGESTIONS.FIND_BY_PROPERTY,
REPOSITORY_METHOD_SUGGESTIONS.GET_ENTITY,
],
load: [
REPOSITORY_METHOD_SUGGESTIONS.FIND_BY_PROPERTY,
REPOSITORY_METHOD_SUGGESTIONS.GET_ENTITY,
],
}
for (const [keyword, keywords] of Object.entries(suggestionMap)) {
if (lowerName.includes(keyword)) {
suggestions.push(...keywords)
}
}
}
}