Validate DDD architecture compliance including layer separation, dependency rules, and patterns...
Validate Domain-Driven Design layer separation, dependency rules, and architectural patterns. Non-destructive analysis providing actionable recommendations.
Performs comprehensive analysis of DDD architecture:
@injectable() usageUse when you need to:
Examples:
The validator enforces this hierarchy:
Presentation → Application → Domain
↓ ↓
Infrastructure
Rules:
Zero Dependencies:
Entity Patterns:
create() methodreconstitute() methodValue Object Patterns:
create() with validationequals() methodRepository Interfaces:
UseCase Patterns:
@injectable() decorator presentexecute() methodImport Restrictions:
Repository Implementations:
@injectable() decoratorMapper Patterns:
toDomain() methodtoPersistence() methodController Patterns:
@injectable() decoratorRoute Patterns:
/v1/.bind()Violations that break core DDD principles:
CRITICAL: Domain importing framework dependency
File: src/contexts/user/domain/entities/user.entity.ts:3
Issue: Importing 'injectable' from tsyringe in domain layer
Fix: Remove @injectable() from entity. Only use in application/infrastructure/presentation.
CRITICAL: Public entity constructor
File: src/contexts/user/domain/entities/user.entity.ts:10
Issue: Constructor is public, should be private
Fix: Change to private constructor with static factory methods
Deviations from best practices:
WARNING: Missing reconstitute method
File: src/contexts/user/domain/entities/user.entity.ts
Issue: Only create() found, missing reconstitute()
Recommendation: Add static reconstitute() for loading from database
Improvements:
SUGGESTION: Extract Email into value object
File: src/contexts/user/domain/entities/user.entity.ts:15
Current: Using string for email
Recommendation: Create Email value object with validation
Framework Imports:
// ❌ WRONG
import { injectable } from 'tsyringe';
import { Schema } from 'mongoose';
// ✅ CORRECT
import { randomUUID } from 'crypto';
import { Email } from '../value-objects/email.vo';
Public Constructor:
// ❌ WRONG
export class User {
constructor(public id: string, public name: string) {}
}
// ✅ CORRECT
export class User {
private constructor(
private readonly id: string,
private name: string
) {}
static create(data: CreateData): User { ... }
static reconstitute(data: PersistedData): User { ... }
}
Mutable Value Object:
// ❌ WRONG
export class Email {
private value: string; // Not readonly
}
// ✅ CORRECT
export class Email {
private readonly value: string;
}
Wrong Injection Pattern:
// ❌ WRONG - Repository by class
constructor(
@inject(UserRepository)
private repo: IUserRepository
) {}
// ✅ CORRECT - Repository by token
constructor(
@inject('IUserRepository')
private repo: IUserRepository
) {}
HTTP Concerns:
// ❌ WRONG
async execute(input: Input): Promise<Output> {
throw new HttpException(404, 'Not found');
}
// ✅ CORRECT
async execute(input: Input): Promise<Output> {
throw new EntityNotFoundError(id);
}
Controller Injecting Repository:
// ❌ WRONG
constructor(
@inject('IUserRepository')
private repo: IUserRepository
) {}
// ✅ CORRECT
constructor(
@inject(CreateUserUseCase)
private createUser: CreateUserUseCase
) {}
Not Binding Controller:
// ❌ WRONG
.post('/', controller.create)
// ✅ CORRECT
.post('/', controller.create.bind(controller))
The validator is read-only - it never modifies code. After review: