dotnet-cqrs/docs/tutorials/modular-solution/03-cqrs-layer.md

3.5 KiB

CQRS Layer

Implement commands, queries, and handlers.

Create Command

// OrderManagement.CQRS/Commands/PlaceOrderCommand.cs
namespace OrderManagement.CQRS.Commands;

public record PlaceOrderCommand
{
    public int CustomerId { get; init; }
    public List<OrderItemDto> Items { get; init; } = new();
}

public record OrderItemDto
{
    public int ProductId { get; init; }
    public string ProductName { get; init; } = string.Empty;
    public int Quantity { get; init; }
    public decimal Price { get; init; }
}

Command Handler

// OrderManagement.CQRS/Commands/PlaceOrderCommandHandler.cs
using Svrnty.CQRS.Abstractions;
using OrderManagement.Domain.Entities;

namespace OrderManagement.CQRS.Commands;

public class PlaceOrderCommandHandler : ICommandHandler<PlaceOrderCommand, int>
{
    private readonly IOrderRepository _repository;

    public PlaceOrderCommandHandler(IOrderRepository repository)
    {
        _repository = repository;
    }

    public async Task<int> HandleAsync(PlaceOrderCommand command, CancellationToken ct)
    {
        var items = command.Items.Select(i => new OrderItem
        {
            ProductId = i.ProductId,
            ProductName = i.ProductName,
            Quantity = i.Quantity,
            Price = i.Price
        }).ToList();

        var order = Order.Create(command.CustomerId, items);

        await _repository.AddAsync(order, ct);
        await _repository.SaveChangesAsync(ct);

        return order.Id;
    }
}

Validator

// OrderManagement.CQRS/Validators/PlaceOrderCommandValidator.cs
using FluentValidation;

namespace OrderManagement.CQRS.Validators;

public class PlaceOrderCommandValidator : AbstractValidator<PlaceOrderCommand>
{
    public PlaceOrderCommandValidator()
    {
        RuleFor(x => x.CustomerId).GreaterThan(0);
        RuleFor(x => x.Items).NotEmpty();
        RuleForEach(x => x.Items).ChildRules(item =>
        {
            item.RuleFor(x => x.ProductId).GreaterThan(0);
            item.RuleFor(x => x.Quantity).GreaterThan(0);
            item.RuleFor(x => x.Price).GreaterThan(0);
        });
    }
}

Query

// OrderManagement.CQRS/Queries/GetOrderQuery.cs
public record GetOrderQuery
{
    public int OrderId { get; init; }
}

public record OrderDto
{
    public int Id { get; init; }
    public int CustomerId { get; init; }
    public string Status { get; init; } = string.Empty;
    public decimal TotalAmount { get; init; }
    public DateTimeOffset PlacedAt { get; init; }
    public List<OrderItemDto> Items { get; init; } = new();
}

Query Handler

// OrderManagement.CQRS/Queries/GetOrderQueryHandler.cs
public class GetOrderQueryHandler : IQueryHandler<GetOrderQuery, OrderDto>
{
    private readonly IOrderRepository _repository;

    public async Task<OrderDto> HandleAsync(GetOrderQuery query, CancellationToken ct)
    {
        var order = await _repository.GetByIdAsync(query.OrderId, ct);

        return new OrderDto
        {
            Id = order.Id,
            CustomerId = order.CustomerId,
            Status = order.Status.ToString(),
            TotalAmount = order.TotalAmount,
            PlacedAt = order.PlacedAt,
            Items = order.Items.Select(i => new OrderItemDto
            {
                ProductId = i.ProductId,
                ProductName = i.ProductName,
                Quantity = i.Quantity,
                Price = i.Price
            }).ToList()
        };
    }
}

Next Steps

Continue to DAL Layer