Validating Inside FluentValidation is a hard pass cause it's more headache than needed,
RuleFor(x => x.AccountId).MustAsync(async (id, _) => await _repo.ExistsAsync(id));
why this is a bad idea:
Extra database hits for every validation - not great for performance.
Can lead to the N+1 query problem, especially when dealing with collections.
Runs sequentially, so you lose out on batching optimizations.
Tightly couples validation logic with database access, which isn’t clean.
I'd suggest validating at the service layer
// After standard validation
if (!await _accountRepo.ExistsAsync(expense.AccountId)) return BadRequest();
and it works better cause:
- Optimized queries - I can batch-check multiple IDs at once.
- Single transaction scope - keeps my DB operations efficient.
- Cleaner architecture - validation stays focused on rules, and data integrity is handled where it should be.
I’d say keep FluentValidation for business rules (format, required fields, etc.) and check database constraints in the service layer where I can actually optimize them.
FluentValidation for business rules, service layer for Database checks.
Trust me, your future self will thank you.