Что такое богатая доменная модель (Rich Domain Model)?

«Что такое богатая доменная модель (Rich Domain Model)?» — вопрос из категории Архитектура, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Богатая доменная модель — это архитектурный подход в Domain-Driven Design (DDD), при котором бизнес-логика (поведение) инкапсулирована внутри самих сущностей и объектов-значений предметной области, а не размещается в отдельном сервисном слое (так называемая "анемичная модель").

Суть подхода: Доменные объекты — это не просто "контейнеры для данных" (DTO), а полноценные объекты с методами, которые реализуют правила и инварианты предметной области.

Сравнение с анемичной моделью:

// АНЕМИЧНАЯ МОДЕЛЬ (Anti-pattern): Данные и логика разделены.
public class Order // Только данные, геттеры/сеттеры
{
    public int Id { get; set; }
    public decimal Total { get; set; }
    public List<OrderItem> Items { get; set; } = new();
}
public class OrderService // Вся логика вынесена в сервис
{
    public void AddItem(Order order, Product product, int qty)
    {
        // Проверки и расчеты здесь
        if (qty <= 0) throw new Exception();
        order.Items.Add(new OrderItem(product, qty));
        order.Total += product.Price * qty;
    }
}

// БОГАТАЯ ДОМЕННАЯ МОДЕЛЬ: Логика внутри сущности.
public class Order
{
    public int Id { get; private set; } // Сеттер приватный
    public decimal Total { get; private set; }
    private readonly List<OrderItem> _items = new(); // Инкапсуляция коллекции
    public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();

    // Бизнес-метод, отвечающий за добавление товара. Инкапсулирует правила.
    public void AddItem(Product product, int quantity)
    {
        // 1. Проверка инвариантов (валидация внутри домена)
        if (product == null) throw new ArgumentNullException(nameof(product));
        if (quantity <= 0) throw new ArgumentException("Количество должно быть положительным", nameof(quantity));
        if (!product.IsInStock) throw new InvalidOperationException("Товара нет в наличии");

        // 2. Создание объекта-значения OrderItem
        var item = new OrderItem(product.Id, product.Price, quantity);

        // 3. Изменение состояния сущности
        _items.Add(item);
        Total += item.Subtotal;

        // 4. Возможность генерации доменного события
        // DomainEvents.Raise(new OrderItemAddedEvent(this, item));
    }

    // Другой бизнес-метод
    public void ApplyDiscount(decimal discountPercent)
    {
        if (discountPercent < 0 || discountPercent > 100)
            throw new ArgumentException("Неверный процент скидки");
        Total *= (1 - discountPercent / 100);
    }
}

Преимущества богатой модели:

  • Высокая связность: Данные и операции над ними находятся в одном месте.
  • Инкапсуляция: Состояние объекта защищено от некорректного изменения извне.
  • Выразительность: Код моделирует реальные бизнес-процессы, его легче понимать.
  • Тестируемость: Легко тестировать единицы бизнес-логики в изоляции.

Когда использовать? В сложных проектах с насыщенной и часто меняющейся бизнес-логикой, где важна чистота архитектуры и долгосрочная поддерживаемость.