Что такое DDD (Domain-Driven Design), назовите основные понятия
Domain-Driven Design (DDD) — подход к разработке программного обеспечения, при котором в центре проектирования находится предметная область (домен) бизнеса. Предложен Эриком Эвансом в книге «Domain-Driven Design: Tackling Complexity in the Heart of Software» (2003).
DDD особенно полезен в проектах со сложной бизнес-логикой, где бизнес-правила являются главной ценностью системы (банковские приложения, страхование, логистика).
Стратегические паттерны
Ubiquitous Language (Единый язык)
Общий язык, разделяемый разработчиками и бизнес-экспертами. Термины из этого языка используются в коде, документации и разговорах. Например, в банковском контексте: «Платёжное поручение», «Корреспондентский счёт», «Операционный день».
Пример
// Единый язык в коде: не "processItem()", а "executePaymentOrder()"
public class PaymentOrder { // Платёжное поручение
private Money amount; // Сумма
private Account payerAccount; // Счёт плательщика
private Account payeeAccount; // Счёт получателя
public void execute() { ... } // Исполнить
public void reject(String reason) { ... } // Отклонить
}
Bounded Context (Ограниченный контекст)
Явная граница, внутри которой определённая модель однозначна и согласована. Один и тот же термин может означать разные вещи в разных контекстах.
Пример
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Контекст │ │ Контекст │ │ Контекст │
│ «Платежи» │ │ «Клиенты» │ │ «Риски» │
│ │ │ │ │ │
│ Account = │ │ Account = │ │ Account = │
│ расчётный счёт │ │ учётная запись │ │ объект скоринга │
│ с балансом │ │ клиента в ЛК │ │ │
└──────────────────┘ └──────────────────┘ └──────────────────┘
Тактические паттерны
Entity (Сущность)
Объект, обладающий уникальной идентичностью, которая сохраняется на протяжении всего жизненного цикла. Две сущности с одинаковыми полями, но разными ID — это разные сущности.
Пример кода
public class Account {
private final AccountId id; // идентичность
private Money balance;
private AccountStatus status;
// Бизнес-поведение внутри сущности (Rich Domain Model)
public void withdraw(Money amount) {
if (status != AccountStatus.ACTIVE)
throw new AccountNotActiveException(id);
if (balance.isLessThan(amount))
throw new InsufficientFundsException(id, amount);
balance = balance.minus(amount);
}
}
Value Object (Объект-значение)
Объект, определяемый своими атрибутами, а не идентичностью. Два Value Object с одинаковыми полями — это один и тот же объект. Неизменяем (immutable).
Пример кода
public final class Money {
private final BigDecimal amount;
private final Currency currency;
public Money(BigDecimal amount, Currency currency) {
this.amount = amount;
this.currency = currency;
}
public Money plus(Money other) {
if (!currency.equals(other.currency))
throw new CurrencyMismatchException();
return new Money(amount.add(other.amount), currency);
}
// equals() и hashCode() по всем полям
}
Aggregate (Агрегат)
Кластер связанных сущностей и Value Objects, объединённых под одним корнем (Aggregate Root). Внешний мир взаимодействует с агрегатом только через корень. Агрегат гарантирует согласованность инвариантов.
Пример кода
// Aggregate Root
public class Order {
private OrderId id;
private List<OrderItem> items; // часть агрегата
private Money totalAmount;
public void addItem(Product product, int quantity) {
items.add(new OrderItem(product, quantity));
recalculateTotal();
}
private void recalculateTotal() {
totalAmount = items.stream()
.map(OrderItem::getSubtotal)
.reduce(Money.ZERO, Money::plus);
}
}
Другие тактические паттерны
- Repository (Репозиторий) — абстракция для доступа к агрегатам. Создаёт иллюзию коллекции в памяти. Интерфейс определяется в домене, реализация — в инфраструктуре.
- Domain Event (Доменное событие) — событие, значимое для бизнеса, которое произошло в домене:
PaymentExecuted,AccountBlocked,LimitExceeded. - Domain Service (Доменный сервис) — бизнес-логика, которая не принадлежит ни одной конкретной сущности (например, конвертация валют между двумя счетами).
На собеседовании: Интервьюер хочет услышать разницу между стратегическими (Bounded Context, Ubiquitous Language) и тактическими (Entity, Value Object, Aggregate) паттернами. Частая ошибка — знать только тактические паттерны и не понимать, зачем нужен Bounded Context.