@samiyev/guardian
Version:
Research-backed code quality guardian for AI-assisted development. Detects hardcodes, secrets, circular deps, framework leaks, entity exposure, and 9 architecture violations. Enforces Clean Architecture/DDD principles. Works with GitHub Copilot, Cursor, W
131 lines • 5.22 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AggregateBoundaryDetector = void 0;
const AggregateBoundaryViolation_1 = require("../../domain/value-objects/AggregateBoundaryViolation");
const rules_1 = require("../../shared/constants/rules");
const AggregatePathAnalyzer_1 = require("../strategies/AggregatePathAnalyzer");
const FolderRegistry_1 = require("../strategies/FolderRegistry");
const ImportValidator_1 = require("../strategies/ImportValidator");
/**
* Detects aggregate boundary violations in Domain-Driven Design
*
* This detector enforces DDD aggregate rules:
* - Aggregates should reference each other only by ID or Value Objects
* - Direct entity references across aggregates create tight coupling
* - Each aggregate should be independently modifiable
*
* Folder structure patterns detected:
* - domain/aggregates/order/Order.ts
* - domain/order/Order.ts (aggregate name from parent folder)
* - domain/entities/order/Order.ts
*
* @example
* ```typescript
* const detector = new AggregateBoundaryDetector()
*
* // Detect violations in order aggregate
* const code = `
* import { User } from '../user/User'
* import { UserId } from '../user/value-objects/UserId'
* `
* const violations = detector.detectViolations(
* code,
* 'src/domain/aggregates/order/Order.ts',
* 'domain'
* )
*
* // violations will contain 1 violation for direct User entity import
* // but not for UserId (value object is allowed)
* console.log(violations.length) // 1
* ```
*/
class AggregateBoundaryDetector {
folderRegistry;
pathAnalyzer;
importValidator;
constructor() {
this.folderRegistry = new FolderRegistry_1.FolderRegistry();
this.pathAnalyzer = new AggregatePathAnalyzer_1.AggregatePathAnalyzer(this.folderRegistry);
this.importValidator = new ImportValidator_1.ImportValidator(this.folderRegistry, this.pathAnalyzer);
}
/**
* Detects aggregate boundary violations in the given code
*
* Analyzes import statements to identify direct entity references
* across aggregate boundaries in the domain layer.
*
* @param code - Source code to analyze
* @param filePath - Path to the file being analyzed
* @param layer - The architectural layer of the file (should be 'domain')
* @returns Array of detected aggregate boundary violations
*/
detectViolations(code, filePath, layer) {
if (layer !== rules_1.LAYERS.DOMAIN) {
return [];
}
const currentAggregate = this.pathAnalyzer.extractAggregateFromPath(filePath);
if (!currentAggregate) {
return [];
}
return this.analyzeImports(code, filePath, currentAggregate);
}
/**
* Checks if a file path belongs to an aggregate
*
* Extracts aggregate name from paths like:
* - domain/aggregates/order/Order.ts → 'order'
* - domain/order/Order.ts → 'order'
* - domain/entities/order/Order.ts → 'order'
*
* @param filePath - The file path to check
* @returns The aggregate name if found, undefined otherwise
*/
extractAggregateFromPath(filePath) {
return this.pathAnalyzer.extractAggregateFromPath(filePath);
}
/**
* Checks if an import path references an entity from another aggregate
*
* @param importPath - The import path to analyze
* @param currentAggregate - The aggregate of the current file
* @returns True if the import crosses aggregate boundaries inappropriately
*/
isAggregateBoundaryViolation(importPath, currentAggregate) {
return this.importValidator.isViolation(importPath, currentAggregate);
}
/**
* Analyzes all imports in code and detects violations
*/
analyzeImports(code, filePath, currentAggregate) {
const violations = [];
const lines = code.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const lineNumber = i + 1;
const imports = this.importValidator.extractImports(line);
for (const importPath of imports) {
const violation = this.checkImport(importPath, currentAggregate, filePath, lineNumber);
if (violation) {
violations.push(violation);
}
}
}
return violations;
}
/**
* Checks a single import for boundary violations
*/
checkImport(importPath, currentAggregate, filePath, lineNumber) {
if (!this.importValidator.isViolation(importPath, currentAggregate)) {
return undefined;
}
const targetAggregate = this.pathAnalyzer.extractAggregateFromImport(importPath);
const entityName = this.pathAnalyzer.extractEntityName(importPath);
if (targetAggregate && entityName) {
return AggregateBoundaryViolation_1.AggregateBoundaryViolation.create(currentAggregate, targetAggregate, entityName, importPath, filePath, lineNumber);
}
return undefined;
}
}
exports.AggregateBoundaryDetector = AggregateBoundaryDetector;
//# sourceMappingURL=AggregateBoundaryDetector.js.map