Reputation: 27
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!
Upvotes: 0
Views: 1068
Reputation: 57204
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).
Upvotes: 1