Ответ
Application слой (уровень приложений) — это слой в многослойной архитектуре (например, Clean Architecture, Onion), который отвечает за координацию рабочих процессов приложения и реализацию сценариев использования (Use Cases). Он служит посредником между внешними интерфейсами (Presentation/UI) и доменной логикой (Domain Layer).
Ключевые обязанности:
- Оркестрация: Координация вызовов к доменным объектам, репозиториям и внешним сервисам для выполнения конкретного сценария.
- Транзакционность: Управление границами транзакций для операции.
- Валидация входных данных: Проверка DTO (Data Transfer Objects) на корректность с точки зрения приложения.
- Преобразование данных: Маппинг между доменными моделями и DTO для ввода/вывода.
Пример Application Service на C#:
public class PlaceOrderService // Application Service
{
private readonly IOrderRepository _orderRepository;
private readonly IInventoryClient _inventoryClient;
public PlaceOrderService(IOrderRepository orderRepository, IInventoryClient inventoryClient)
{
_orderRepository = orderRepository;
_inventoryClient = inventoryClient;
}
public async Task<OrderResultDto> Execute(PlaceOrderCommand command)
{
// 1. Валидация входных данных (Application concern)
if (command.Items == null || !command.Items.Any())
throw new InvalidRequestException("Order must contain items.");
// 2. Координация доменной логики и инфраструктуры
var productIds = command.Items.Select(i => i.ProductId).ToList();
var areAvailable = await _inventoryClient.CheckAvailability(productIds);
if (!areAvailable)
throw new DomainException("Products are out of stock.");
// 3. Создание и сохранение доменного агрегата (Domain concern)
var order = new Order(command.CustomerId);
foreach (var item in command.Items)
{
order.AddItem(item.ProductId, item.Quantity);
}
await _orderRepository.AddAsync(order);
await _orderRepository.UnitOfWork.SaveChangesAsync(); // Управление транзакцией
// 4. Возврат DTO (Application concern)
return new OrderResultDto { OrderId = order.Id, Status = order.Status };
}
}
Важно: Application слой не содержит сложной бизнес-логики (это Domain Layer) и инфраструктурных деталей (доступ к БД, вызов API — это Infrastructure Layer).
Ответ 18+ 🔞
Давай разберём эту вашу многослойную архитектуру, а то как посмотришь на некоторые проекты — волосы дыбом, там всё в одну кучу свалено, будто после хорошей пьянки код писали.
Смотри, есть у тебя домен — это святое, ядро, где живут твои бизнес-правила, самые важные сущности. Их нельзя ничем засирать, никакими базами или апишками. А есть внешний мир — контроллеры, база, другие сервисы. Так вот, между ними нужно поставить надёжного буфера, который будет всем этим хозяйством рулить, но сам при этом в бизнес-логику лезть не будет. Это и есть Application слой, или, как его ещё обзывают, слой приложений.
Чем он, бедолага, занимается, пока ты кофе пьёшь:
- Оркестрация, блядь. Он как дирижёр в оркестре: сам на скрипке не играет, но командует, когда доменным объектам вступать, когда репозиторию данные тащить, а когда внешнему сервису чекнуть наличие товара. Просто координирует процесс для конкретного сценария (Use Case).
- Транзакционность. Он следит, чтобы операция либо полностью выполнилась, либо откатилась, как будто её и не было. Нельзя же половину заказа создать, а потом упасть с ошибкой — это пиздец пользователю.
- Валидация входных данных. Не ту бизнес-валидацию, что «скидка не больше 100%» (это домен), а прикладную: «а команда-то вообще пришла?», «а список товаров не пустой?». Элементарные проверки, чтобы мусор в домен не тащить.
- Преобразование данных. Он знает, как твой красивый доменный
Orderпревратить в убогийOrderResultDtoдля фронта, и наоборот. Домену-то похуй на эти DTO, ему бы свои инварианты сохранить.
А вот чего он НЕ делает, ни под каким соусом:
- Не содержит сложной бизнес-логики. Это не его епархия. Его дело — позвать нужный доменный метод в нужное время.
- Не лезет в инфраструктурные детали. Как именно данные из БД достаются или как дергается внешнее АПИ — его не ебёт. Он работает через абстракции (интерфейсы).
Смотри, как это выглядит в коде, на примере сервиса оформления заказа:
public class PlaceOrderService // Это он, Application Service
{
// Зависимости через интерфейсы. Инфраструктура? Похуй, главное — контракт.
private readonly IOrderRepository _orderRepository;
private readonly IInventoryClient _inventoryClient;
public PlaceOrderService(IOrderRepository orderRepository, IInventoryClient inventoryClient)
{
_orderRepository = orderRepository;
_inventoryClient = inventoryClient;
}
public async Task<OrderResultDto> Execute(PlaceOrderCommand command)
{
// 1. Прикладная валидация: "Чувак, ты мне вообще что-то прислал?"
if (command.Items == null || !command.Items.Any())
throw new InvalidRequestException("Order must contain items.");
// 2. Оркестрация: сначала дергаем внешний сервис проверить наличие
var productIds = command.Items.Select(i => i.ProductId).ToList();
var areAvailable = await _inventoryClient.CheckAvailability(productIds);
if (!areAvailable)
throw new DomainException("Products are out of stock."); // Доменную исключил, но кинул её АППЛИКЕЙШН
// 3. Работа с доменом: создаём агрегат, используем его логику
var order = new Order(command.CustomerId);
foreach (var item in command.Items)
{
order.AddItem(item.ProductId, item.Quantity); // Вот тут доменный метод!
}
// 4. Сохраняем через абстракцию и фиксируем транзакцию
await _orderRepository.AddAsync(order);
await _orderRepository.UnitOfWork.SaveChangesAsync();
// 5. Возвращаем результат, понятный внешнему миру (DTO)
return new OrderResultDto { OrderId = order.Id, Status = order.Status };
}
}
Короче, Application слой — это твой надёжный управляющий. Он не умничает с бизнес-правилами (это удел домена) и не парится, как там база данных устроена (это инфраструктура). Его задача — грамотно, в правильном порядке, позвать нужных ребят на сцену, чтобы сценарий использования отработал чётко, как швейцарские часы. А если начнёт в чужое лезть — получится та самая каша, где всё связано со всем, и потом любое изменение стоит, как чугунный мост.