简体   繁体   中英

How to keep the command handler clean of querying logic? (CQRS and MediatR)

The problem that I am dealing with is how to keep my controllers free of querying logic that really should be an Infrastructure rather that Application concern.

Down below is an example of Command and CommandHandler. I am using MediatR by the way.

As you can see, the Command contains some filters (which I think is ok, that does not violate CQRS) but the Command Handler has too much logic in it. I am not sure that is something that the handler has to deal with.

GetMatchesCommand

public class GetMatchesCommand : IRequest<IEnumerable<MatchDto>>
{
    public Tuple<DateTime, DateTime> TimeInterval { get; set; }
    public Guid? Location { get; set; }
    public Guid? Team { get; set; }
    public Guid? Player { get; set; }
}

GetMatchesCommandHandler

public class GetMatchesCommandHandler : IRequestHandler<GetMatchesCommand, IEnumerable<MatchDto>>
{       
    public async Task<IEnumerable<MatchDto>> Handle(GetMatchesCommand request, CancellationToken cancellationToken)
    {
        Expression<Func<Match, bool>> filterExpression = default;
            
        // Build the filter expression

        // Filter by interval
        if (request.TimeInterval != null)
            filterExpression.ConcatAnd(
                m => m.StartTime >= request.TimeInterval.Item1
                && (m.StartTime + m.Duration) <= request.TimeInterval.Item2);

        // Filter by team
        if (request.Team != null)
            filterExpression.ConcatAnd(
                m => m.Team1Id == request.Team
                || m.Team2Id == request.Team);

        // Filter by player
        if (request.Player != null)
            filterExpression.ConcatAnd(
                m => m.Team1.Players.Any(p => p.Id == request.Player)
                || m.Team2.Players.Any(p => p.Id == request.Player));

        var query = _dbContext.Matches
            .Include(m => m.Team1).ThenInclude(t => t.Players)
            .Include(m => m.Team2).ThenInclude(t => t.Players);

        // if there are any filters, apply them
        if(filterExpression != null)
        {
            query.Where(filterExpression);
        }

        var matches = query.ToListAsync();
        return _mapper.Map<List<MatchDto>>(matches);
    }
}

I know that the repository pattern might be suitable for this case, but still, what would be the right way of doing it? Having a _matchesRepo.Get(interval, team, player, location) and moving this logic there does not seem to be a very smart approach to be honest...

Can someone give me an advice on this? Thank you in advance!

GetMatches should probably be understood as a query , rather than a command , based on the example you have provided here.

The key hint being that your implementation is effectively read only - you aren't making changes to your model and saving them in the repository.

Filtering in a query handler is a normal thing to do.

You could probably simplify the design here by encapsulating the construction of the filter expression into another function (separation of concerns / information hiding).

I am doing the similar project about football match, can you share your code and expericence in design database and UI.

Have you finished it. I also want to write the same function and using AJAX to get result (list of Match) when user typing on textbox but someone told me about performance when you send request to server on eachtime button downevent (user typing one more charactor)

Thanks.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM