CQRS sounds like two databases, messages and lagging read models. Most applications that benefit from CQRS need none of that. It is enough to put commands and queries on two separate paths through the module, in the same database, from the same tables. This light form is the normal case, and in this lesson you build it for the Kladde's contact list.
You already know the tool from lesson 2.4.9: AsNoTracking and projections with Select. What is new is where these queries sit in the modular
monolith, what shape their port has and why they may bypass the domain model.
How the contact list reads today
After module 4.3, the Contacts module has a port IContactRepository (lesson 4.3.5). The “Contacts” page and the endpoint for changes
both use it: the list loads entities of the type Contact through the repository and builds its rows from them. That has three consequences:
- Too many columns. A
Contacthas notes, phone number, address. The list shows name, company, email address and the last contact. The rest is read, transferred, turned into objects and thrown away. - Change tracking for nothing. For every loaded entity, EF Core remembers a copy so that it can find the changes at
SaveChanges. A list never saves. - The repository grows in the wrong direction. The list needs a search, pages, a sort order, later a filter by company. A repository for aggregates is supposed to load and save, not serve every display.
The third point is the most important. The first two cost time and memory; the third costs clarity.