Что такое доменный слой (Domain Layer) в архитектуре приложения?

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

Ответ

Доменный слой (Domain Layer) — это центральный, наиболее важный слой в архитектурах, основанных на предметной области (например, Domain-Driven Design, Чистая Архитектура). Он инкапсулирует ключевую бизнес-логику, правила и сущности приложения, полностью абстрагируясь от деталей инфраструктуры (база данных, UI, фреймворки) и внешних сервисов.

Его основная цель: Чёткое выражение бизнес-концепций и правил в коде, чтобы они были понятны как разработчикам, так и экспертам предметной области.

Ключевые строительные блоки доменного слоя:

  • Сущности (Entities): Объекты, имеющие уникальную идентичность, которая сохраняется на протяжении всего жизненного цикла (например, Customer, Order, Invoice). Они содержат как данные, так и поведение (методы, инкапсулирующие бизнес-правила).
  • Объекты-значения (Value Objects): Неизменяемые объекты, не имеющие идентификатора и определяемые исключительно своими атрибутами (например, Address, Money, Color). Два Value Object с одинаковыми значениями считаются равными.
  • Агрегаты (Aggregates): Кластеры связанных сущностей и объектов-значений, которые рассматриваются как единое целое для операций изменения данных. У агрегата есть корень (Aggregate Root) — единственная сущность, через которую происходит всё внешнее взаимодействие с агрегатом.
  • Доменные сервисы (Domain Services): Классы, содержащие бизнес-логику, которая по своей природе не принадлежит ни одной конкретной сущности или объекту-значению (например, сложный расчёт, затрагивающий несколько агрегатов).
  • Репозитории (Repository Interfaces): Абстракции (интерфейсы), определяющие контракты для доступа к агрегатам. Их реализация находится в слое инфраструктуры.
  • События домена (Domain Events): Способ информирования других частей системы о значимых изменениях, произошедших внутри домена.

Пример сущности (Aggregate Root) на C#:

public class Order : Entity<int> // Entity с Id типа int
{
    // Внутреннее состояние. Коллекция защищена от прямого изменения.
    private readonly List<OrderLine> _lines = new();
    public CustomerId CustomerId { get; private set; } // Value Object для Id
    public OrderStatus Status { get; private set; }
    public IReadOnlyCollection<OrderLine> Lines => _lines.AsReadOnly();

    // Публичные методы — это единственный способ изменить состояние.
    // Они инкапсулируют бизнес-правила.
    public void AddItem(Product product, int quantity)
    {
        // Правило 1: Нельзя добавлять товары в завершённый заказ.
        if (Status == OrderStatus.Completed || Status == OrderStatus.Cancelled)
            throw new InvalidOrderOperationException("Cannot modify a completed/cancelled order.");

        // Правило 2: Количество должно быть положительным.
        if (quantity <= 0)
            throw new ArgumentException("Quantity must be greater than zero.", nameof(quantity));

        // Правило 3: Если товар уже есть в заказе, увеличиваем количество.
        var existingLine = _lines.FirstOrDefault(l => l.ProductId == product.Id);
        if (existingLine != null)
        {
            existingLine.IncreaseQuantity(quantity);
        }
        else
        {
            _lines.Add(new OrderLine(product.Id, product.Price, quantity));
        }
    }

    public void MarkAsCompleted()
    {
        // Правило: Нельзя завершить пустой заказ.
        if (!_lines.Any())
            throw new InvalidOrderOperationException("Cannot complete an empty order.");
        Status = OrderStatus.Completed;
        // Может генерировать Domain Event: new OrderCompletedEvent(this.Id)
    }
}

Преимущества выделенного доменного слоя:

  • Тестируемость: Логику можно тестировать изолированно, без БД или веб-сервера.
  • Сохраняемость: Бизнес-правила не "растворяются" в коде инфраструктуры.
  • Гибкость: Слой инфраструктуры (способ хранения данных) можно заменить, не затрагивая ядро бизнес-логики.
  • Ясность: Код напрямую отражает бизнес-концепции, упрощая коммуникацию в команде.