Hard📖Теория2 min

Шаг 10: Queries -- чтение данных

Создание GetOrderQuery и GetCustomerOrdersQuery: чем Query отличается от Command, маппинг Domain -> DTO и LINQ-преобразования

Шаг 10: Queries -- чтение данных

Query не изменяет данные, а только читает и возвращает DTO. Это вторая сторона CQRS.

GetOrderQuery

В папке Orders/Queries/GetOrder:

using MediatR;
using OrderManagement.Application.Orders.DTOs;

namespace OrderManagement.Application.Orders.Queries.GetOrder;

public record GetOrderQuery(Guid OrderId) : IRequest<OrderDto>;

public class GetOrderQueryHandler
    : IRequestHandler<GetOrderQuery, OrderDto>
{
    private readonly IOrderRepository _orderRepository;

    public GetOrderQueryHandler(IOrderRepository orderRepository)
        => _orderRepository = orderRepository;

    public async Task<OrderDto> Handle(
        GetOrderQuery query, CancellationToken ct)
    {
        var order = await _orderRepository
            .GetByIdAsync(query.OrderId, ct)
            ?? throw new OrderNotFoundException(query.OrderId);

        return new OrderDto(
            order.Id, order.CustomerId,
            order.Status.ToString(),
            order.ShippingAddress.ToString(),
            order.TotalAmount.Amount,
            order.TotalAmount.Currency,
            order.CreatedAt,
            order.Items.Select(i => new OrderItemDto(
                i.Id, i.ProductId, i.ProductName,
                i.UnitPrice.Amount, i.Quantity,
                i.TotalPrice.Amount)).ToList());
    }
}

Маппинг Order -> OrderDto преобразует доменную сущность в объект для клиента. Status.ToString() конвертирует enum в строку "Confirmed". LINQ .Select() преобразует каждый OrderItem в OrderItemDto.

GetCustomerOrdersQuery

public record GetCustomerOrdersQuery(string CustomerId)
    : IRequest<List<OrderDto>>;

public class GetCustomerOrdersQueryHandler
    : IRequestHandler<GetCustomerOrdersQuery, List<OrderDto>>
{
    private readonly IOrderRepository _orderRepository;

    public GetCustomerOrdersQueryHandler(
        IOrderRepository orderRepository)
        => _orderRepository = orderRepository;

    public async Task<List<OrderDto>> Handle(
        GetCustomerOrdersQuery query, CancellationToken ct)
    {
        var orders = await _orderRepository
            .GetByCustomerIdAsync(query.CustomerId, ct);

        return orders.Select(order => new OrderDto(
            order.Id, order.CustomerId,
            order.Status.ToString(),
            order.ShippingAddress.ToString(),
            order.TotalAmount.Amount,
            order.TotalAmount.Currency,
            order.CreatedAt,
            order.Items.Select(i => new OrderItemDto(
                i.Id, i.ProductId, i.ProductName,
                i.UnitPrice.Amount, i.Quantity,
                i.TotalPrice.Amount)).ToList()
        )).ToList();
    }
}

Проверь себя

🧪

Чем Query Handler отличается от Command Handler?

🧪

Что делает метод LINQ Select в контексте маппинга?

🧪

Зачем маппить Order в OrderDto, а не возвращать Order напрямую?