Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Domain-Driven Design (DDD): полное руководство
Domain-Driven Design — это философия проектирования программного обеспечения, которая ставит бизнес-логику (domain) в центр разработки. Это не просто набор паттернов, это способ думать о коде.
Основная идея DDD
Вместо того, чтобы организовать код по техническим слоям, мы организуем его по бизнес-доменам. Каждый домен содержит всю необходимую логику для его функционирования.
ПЛОХО: техническое разделение
src/
controllers/
services/
repositories/
ХОРОШО: разделение по доменам
src/
features/
user/
domain/
User.ts
application/
CreateUserUseCase.ts
infrastructure/
UserRepositoryImpl.ts
Четыре слоя DDD
1. Presentation Layer — как пользователь общается
export class UserController {
constructor(private createUserUseCase: CreateUserUseCase) {}
async create(req: Request, res: Response) {
const user = await this.createUserUseCase.execute(req.body);
res.json(user);
}
}
2. Application Layer — координация между доменом и внешним миром
export class CreateUserUseCase {
constructor(
private userRepository: IUserRepository,
private emailService: IEmailService
) {}
async execute(command: CreateUserCommand): Promise<User> {
const existing = await this.userRepository.findByEmail(command.email);
if (existing) {
throw new EmailAlreadyExistsError();
}
const user = new User(
generateId(),
command.email,
command.password,
new Date()
);
await this.userRepository.save(user);
await this.emailService.sendWelcome(user.email);
return user;
}
}
3. Domain Layer — чистая бизнес-логика (сердце приложения)
export class User {
constructor(
private id: string,
private email: string,
private hashedPassword: string,
private createdAt: Date
) {}
authenticate(plainPassword: string): boolean {
return bcrypt.compareSync(plainPassword, this.hashedPassword);
}
updateEmail(newEmail: string): void {
if (!this.isValidEmail(newEmail)) {
throw new InvalidEmailError();
}
this.email = newEmail;
}
private isValidEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
getId(): string { return this.id; }
getEmail(): string { return this.email; }
}
4. Infrastructure Layer — технические детали
export class UserRepositoryImpl implements IUserRepository {
constructor(private db: Database) {}
async save(user: User): Promise<void> {
await this.db.query(
'INSERT INTO users (id, email, password, created_at) VALUES ($1, $2, $3, $4)',
[user.getId(), user.getEmail(), user.getHashedPassword(), user.getCreatedAt()]
);
}
async findByEmail(email: string): Promise<User | null> {
const row = await this.db.query(
'SELECT * FROM users WHERE email = $1',
[email]
);
if (!row) return null;
return new User(row.id, row.email, row.password, row.created_at);
}
}
Ключевые концепции DDD
1. Aggregates — группировка сущностей
export class Post {
private id: string;
private comments: Comment[] = [];
addComment(comment: Comment): void {
if (this.comments.length >= 100) {
throw new MaxCommentsExceededError();
}
this.comments.push(comment);
}
getComments(): Comment[] {
return [...this.comments];
}
}
2. Value Objects — объекты без идентичности
export class Email {
constructor(private value: string) {
if (!this.isValid(value)) {
throw new InvalidEmailError();
}
}
private isValid(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
equals(other: Email): boolean {
return this.value === other.value;
}
toString(): string {
return this.value;
}
}
3. Domain Events — коммуникация между доменами
export class UserRegisteredEvent {
constructor(
public userId: string,
public email: string,
public timestamp: Date
) {}
}
export class User {
private domainEvents: DomainEvent[] = [];
static create(email: string, password: string): User {
const user = new User(generateId(), email, password, new Date());
user.addDomainEvent(new UserRegisteredEvent(
user.id,
user.email,
new Date()
));
return user;
}
getDomainEvents(): DomainEvent[] {
return this.domainEvents;
}
}
export class CreateUserUseCase {
async execute(command: CreateUserCommand): Promise<User> {
const user = User.create(command.email, command.password);
await this.userRepository.save(user);
for (const event of user.getDomainEvents()) {
await this.eventPublisher.publish(event);
}
return user;
}
}
DDD в реальном проекте: E-commerce
src/
features/
orders/
domain/
Order.ts
OrderLine.ts
OrderStatus.ts
application/
CreateOrderUseCase.ts
infrastructure/
OrderRepositoryImpl.ts
presentation/
OrderController.ts
products/
domain/
Product.ts
Price.ts
application/
FindProductUseCase.ts
infrastructure/
ProductRepositoryImpl.ts
Преимущества DDD
- Простота в понимании: код отражает бизнес-логику
- Меньше bugs: бизнес-правила инкапсулированы
- Масштабируемость: можно split на микросервисы
- Поддерживаемость: изменения в одном domain не влияют на другие
- Переиспользуемость: domain logic независим от transport layer
Когда использовать DDD
- Большие проекты с сложной бизнес-логикой
- Долгосрочные проекты которые будут развиваться
- Когда команда растет
- Когда есть четкие бизнес-домены
Когда NOT использовать DDD
- Маленькие CRUD приложения
- MVP которые нужно быстро запустить
- Простые скрипты или утилиты