Whetstone.
DDD layers and CQRSQueryStore reads, Repository writes
Module 9 · Lesson 213 min

QueryStore reads, Repository writes

Reads and writes live in different classes, and the naming tells you which is which. A QueryStore does read-only queries: method names start with Get, and every query is .AsNoTracking() (it never intends to mutate, so it skips EF’s change tracker). A Repository loads entities for an operation and saves them: Load* to read-for-write, Save*/MarkAs* to write. Anti-pattern #6: don’t mix AsNoTracking reads and writes in one class, and don’t put Get* on a repository or Load* on a query store.

Read side: AsNoTracking, Get* (real DecisionQueryStore)
public sealed class DecisionQueryStore : IDecisionQueryStore
{
    public async Task<IReadOnlySet<string>> GetDecidedFlightIdentities()
    {
        var keys = await _dbContext.FlightDisruptionCostCalcs
            .AsNoTracking()                                  // read-only, no change tracking
            .Where(x => x.DecidedAtUtc != null && x.RevokedAtUtc == null)
            .Select(x => x.EjdpUniqueFlightKey)
            .ToListAsync();
        return keys.ToHashSet(StringComparer.OrdinalIgnoreCase);
    }
}
Practice

Try it yourself

Quiz

Where does the method go

You need a method that returns a list of decided snapshots for a date range, read-only, for a list view. Where does it belong and what marks it?

  1. AOn the Repository, named LoadDecidedSnapshots, with change tracking
  2. BOn the QueryStore, named GetDecidedSnapshots... , with AsNoTracking
  3. CEither class, it doesn't matter
  4. DOn the aggregate itself
Show answer

Correct answer: B — On the QueryStore, named GetDecidedSnapshots... , with AsNoTracking

A read-only query for a view goes on the QueryStore with a Get* name and AsNoTracking (anti-pattern #6). Load*/Save* and change tracking are the Repository's job for writes.

Recall

Why AsNoTracking

State the reason, not just the rule.

Why does every method in a QueryStore call .AsNoTracking()?

Reveal answer

The results are read-only: they're never mutated and re-saved, so there's no reason to pay EF's change-tracker cost, and it keeps query-path side effects off the write-side aggregate repository. AsNoTracking is faster and signals intent (this is a pure read).

Sign in to track your progress →