A query-optimised projection of domain data
A read model is a denormalised view of data built and shaped for queries. It exists to answer questions fast: the seat map, the order history, the dashboardDashboardData & AnalyticsAn analytics dashboardView reference →. It is derived from the same facts the write side produces, but it stores them in whatever form makes reading cheap, even if that means duplication and pre-joined data. The catch is that it lags. A read model is usually a little behind the truth, and a system that uses one has to be honest about that gap.
The read model is the query half of Command Query Responsibility Segregation, the pattern Greg Young coined around 2010 and Martin Fowler described in CQRS (2011). Fowler's framing is that you can use a different model to update information than the model you use to read it. The write side enforces rules; the read side is free to be a flat, query-shaped projection with none of that machinery.
The idea has older roots in the relational world. A read model is, in spirit, a materialised view: a query result stored and refreshed so reads do not recompute it every time. CQRS made the view a deliberate architectural layer rather than a database optimisation, with its own storage and its own update path.
The strongest pairing is with event sourcing, described by Fowler in Event Sourcing (2005). When the write side stores a log of events, read models are projections built by replaying those events. The same event stream can feed many read models, each tuned to a different screen or report, and a new one can be created at any time by replaying history into it. This is also where eventual consistency enters: the projection updates after the event lands, so the read model trails the write model by some delay the system has to design around.
An e-commerce platform handles thousands of orders an hour. The write side is an Order aggregate that enforces invariants and emits events such as OrderPlaced, OrderShipped, and OrderCancelled. None of those is shaped for the screen a customer-support agent stares at all day, which needsNeedUserA user need, pain, desire, or constraintView reference → order, customer, shipping status, and recent activity in one row.
So a projection listens to the event stream and maintains a CustomerOrderSummary read model: one denormalised record per order, pre-joined across what would otherwise be five tables. The support screen reads that single record in a few milliseconds, with no joins at request time. When an OrderShipped event arrives, the projection updates the summary, typically within a second. If an agent refreshes during that second and sees the order as still processing, the design accepts that small staleness as the cost of fast, isolated reads.
In the Unified Product Graph, a read model is an engineering-region entity for the query side of a system. A Bounded Contextprojected asRead Modelhierarchy edge ties it to the context whose data it serves, bounded_context_projected_as_read_modelRead ModelprojectsAggregatecross-domain records which write-side aggregate it summarises, and read_model_projects_aggregateDomain Eventprojected toRead Modelcausal captures the events that keep it current. Modelling it explicitly makes the read-and-write split visible: you can see which views are derived from which facts, trace where eventual consistency lives, and know exactly what to rebuild when a projection drifts.domain_event_projected_to_read_model
Type-specific fields on BaseNode
projection_sourcestringSource event or aggregate
refresh_strategystringHow the model stays current
idstringrequiredUnique identifier (UUID)
typeNodeTyperequiredDiscriminator for the entity type
titlestringrequiredDisplay name
descriptionstringOptional detailed description
statusstringLifecycle status
tagsstring[]Freeform tags for filtering
3 edge types connected to this entity.
bounded_context_projected_as_read_modeldomain_event_projected_to_read_modelread_model_projects_aggregate