Ответ
Доменный слой (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)
}
}
Преимущества выделенного доменного слоя:
- Тестируемость: Логику можно тестировать изолированно, без БД или веб-сервера.
- Сохраняемость: Бизнес-правила не "растворяются" в коде инфраструктуры.
- Гибкость: Слой инфраструктуры (способ хранения данных) можно заменить, не затрагивая ядро бизнес-логики.
- Ясность: Код напрямую отражает бизнес-концепции, упрощая коммуникацию в команде.