Ответ
Domain-Driven Design (DDD) — это подход к разработке сложного программного обеспечения, который фокусируется на глубоком моделировании предметной области (бизнес-логики) и её отражении в коде. Основная цель — создание гибкой, понятной и легко изменяемой системы, которая эволюционирует вместе с бизнесом.
Ключевые строительные блоки DDD:
- Доменная модель (Domain Model): Сердце системы. Это не просто "база данных", а набор взаимосвязанных объектов (сущностей, value-объектов), которые инкапсулируют бизнес-правила и поведение.
- Универсальный язык (Ubiquitous Language): Общий язык для общения между разработчиками и экспертами предметной области. Термины из этого языка напрямую становятся названиями классов, методов и модулей в коде.
- Ограниченный контекст (Bounded Context): Чёткая граница, внутри которой определённая модель и универсальный язык являются согласованными. Большая система разбивается на такие контексты (например, "Заказы", "Доставка", "Каталог").
- Сущность (Entity): Объект, имеющий уникальный идентификатор, который остаётся неизменным на протяжении всего жизненного цикла (например,
UserсuserId). - Объект-значение (Value Object): Объект, не имеющий идентификатора и определяемый исключительно своими атрибутами (например,
Moneyсamountиcurrency). Они неизменяемы (immutable). - Агрегат (Aggregate): Кластер связанных сущностей и value-объектов, рассматриваемых как единое целое для операций изменения. У агрегата есть корень (Aggregate Root) — единственная сущность, через которую происходит всё внешнее взаимодействие.
- Репозиторий (Repository): Абстракция, предоставляющая доступ к агрегатам, скрывая детали хранения данных (например,
IOrderRepository). - Доменный сервис (Domain Service): Операция или логика, которая по своей природе не принадлежит ни одной сущности или value-объекту (например, сложный расчёт стоимости доставки, затрагивающий несколько агрегатов).
Практический пример на C# (контекст "Заказы"):
// Value Object
public record Address(string Street, string City, string PostalCode);
// Entity
public class OrderItem
{
public ProductId ProductId { get; private set; }
public Money Price { get; private set; }
public int Quantity { get; private set; }
// ... логика создания и изменения
}
// Aggregate Root
public class Order : AggregateRoot<OrderId>
{
private readonly List<OrderItem> _items = new();
public CustomerId CustomerId { get; private set; }
public Address ShippingAddress { get; private set; }
public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();
public void AddItem(Product product, int quantity)
{
// Бизнес-правило: нельзя добавить товар с нулевым количеством
if (quantity <= 0) throw new DomainException("Quantity must be positive.");
var existingItem = _items.FirstOrDefault(i => i.ProductId == product.Id);
if (existingItem != null)
{
// Если товар уже есть, увеличиваем количество
existingItem.IncreaseQuantity(quantity);
}
else
{
// Иначе создаём новый элемент заказа
_items.Add(new OrderItem(product.Id, product.Price, quantity));
}
// Генерируем доменное событие OrderItemAdded
AddDomainEvent(new OrderItemAdded(this.Id, product.Id, quantity));
}
}
// Repository Interface (определяется в доменном слое)
public interface IOrderRepository : IRepository<Order>
{
Task<Order?> GetByIdAsync(OrderId id, CancellationToken cancellationToken = default);
Task AddAsync(Order order, CancellationToken cancellationToken = default);
}
DDD не является серебряной пулей и требует значительных усилий. Он наиболее оправдан в проектах со сложной, постоянно меняющейся бизнес-логикой, где правильность и ясность кода напрямую влияют на успех бизнеса.