feat: add framework leak detection for domain layer

- Add IFrameworkLeakDetector interface in domain/services
- Add FrameworkLeak value object with framework type categorization
- Implement FrameworkLeakDetector with 250+ framework patterns across 12 categories
- Add comprehensive test suite (35 tests) for framework leak detection
- Support HTTP frameworks, ORMs, loggers, caches, message queues, etc.
- Detect framework imports in domain layer and suggest proper abstractions
This commit is contained in:
imfozilbek
2025-11-24 12:53:37 +05:00
parent 32bcf7d465
commit 0e23938e20
4 changed files with 542 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
import { FrameworkLeak } from "../value-objects/FrameworkLeak"
/**
* Interface for detecting framework-specific imports in domain layer
*
* Framework leaks occur when domain layer imports framework-specific packages,
* violating Clean Architecture principles by creating tight coupling to external frameworks.
*/
export interface IFrameworkLeakDetector {
/**
* Detects framework leaks in the given file
*
* @param imports - Array of import paths from the file
* @param filePath - Path to the file being analyzed
* @param layer - The architectural layer of the file (domain, application, infrastructure, shared)
* @returns Array of detected framework leaks
*/
detectLeaks(imports: string[], filePath: string, layer: string | undefined): FrameworkLeak[]
/**
* Checks if a specific import is a framework package
*
* @param importPath - The import path to check
* @returns True if the import is a framework package
*/
isFrameworkPackage(importPath: string): boolean
}

View File

@@ -0,0 +1,112 @@
import { ValueObject } from "./ValueObject"
import { FRAMEWORK_LEAK_MESSAGES } from "../../shared/constants/rules"
interface FrameworkLeakProps {
readonly packageName: string
readonly filePath: string
readonly layer: string
readonly category: string
readonly line?: number
}
/**
* Represents a framework leak violation in the codebase
*
* A framework leak occurs when a domain layer file imports a framework-specific package,
* creating tight coupling and violating Clean Architecture principles.
*
* @example
* ```typescript
* // Bad: Domain layer importing Prisma
* const leak = FrameworkLeak.create(
* '@prisma/client',
* 'src/domain/User.ts',
* 'domain',
* 'ORM',
* 5
* )
*
* console.log(leak.getMessage())
* // "Domain layer imports framework-specific package "@prisma/client". Use interfaces and dependency injection instead."
* ```
*/
export class FrameworkLeak extends ValueObject<FrameworkLeakProps> {
private constructor(props: FrameworkLeakProps) {
super(props)
}
public static create(
packageName: string,
filePath: string,
layer: string,
category: string,
line?: number,
): FrameworkLeak {
return new FrameworkLeak({
packageName,
filePath,
layer,
category,
line,
})
}
public get packageName(): string {
return this.props.packageName
}
public get filePath(): string {
return this.props.filePath
}
public get layer(): string {
return this.props.layer
}
public get category(): string {
return this.props.category
}
public get line(): number | undefined {
return this.props.line
}
public getMessage(): string {
return FRAMEWORK_LEAK_MESSAGES.DOMAIN_IMPORT.replace("{package}", this.props.packageName)
}
public getSuggestion(): string {
return FRAMEWORK_LEAK_MESSAGES.SUGGESTION
}
public getCategoryDescription(): string {
switch (this.props.category) {
case "ORM":
return "Database ORM/ODM"
case "WEB_FRAMEWORK":
return "Web Framework"
case "HTTP_CLIENT":
return "HTTP Client"
case "VALIDATION":
return "Validation Library"
case "DI_CONTAINER":
return "DI Container"
case "LOGGER":
return "Logger"
case "CACHE":
return "Cache"
case "MESSAGE_QUEUE":
return "Message Queue"
case "EMAIL":
return "Email Service"
case "STORAGE":
return "Storage Service"
case "TESTING":
return "Testing Framework"
case "TEMPLATE_ENGINE":
return "Template Engine"
default:
return "Framework Package"
}
}
}