Projections
Your events are the source of truth, but events are a poor thing to read — nobody wants to replay a
thousand MoneyDeposited facts to show an account balance. A projection does that folding for you:
it watches a stream of events and continuously builds a read model — a shaped, queryable view that’s
ready to serve to a UI.
The win is specialization. The same events can feed many projections, each tailored to one screen or question: a balance, a transaction list, a monthly summary. You never reuse one bloated model across conflicting needs — you build a focused read model per view, and each stays simple, fast, and independent.
How you’ll define one
Section titled “How you’ll define one”Projections join events (never other read models), and mapping is automatic by default — name a read model property the same as an event property and it just maps. For a read model backed by events, you usually choose between three styles:
| Style | What it looks like | Reach for it when |
|---|---|---|
| Model-bound | Attributes on the read model record ([FromEvent<T>], [Key], [ChildrenFrom<T>]) | The default. Most projections — it’s the least boilerplate and reads as the model itself. |
| Declarative | A fluent IProjectionFor<T> definition | The mapping needs logic the attributes can’t express cleanly. |
| Reducer | An IReducerFor<T> that receives the event, current state, and event context | The read model is easier to express as state transitions or calculations over previous state. |
Choose a read-model style builds the same read model as a model-bound projection, a declarative projection, and a reducer so the trade-offs are visible side by side.
Choose your consistency
Section titled “Choose your consistency”The one decision worth making up front is when the read model has to be correct:
| Eventual | Immediate | |
|---|---|---|
| When it updates | Shortly after the event is appended (the default) | Synchronously, before the append returns |
| Cost | Cheap, scales freely | More expensive — pay only when you need it |
| Use it for | Almost everything — lists, dashboards, history | A value you must read back correctly right now (e.g. a uniqueness check) |
Start eventual. Promote a projection to immediate only when a workflow genuinely can’t tolerate the read model being a moment behind.
Topics
Section titled “Topics”| Topic | Description |
|---|---|
| Architecture | How the projection engine turns events into read models |
| Choose a read-model style | Compare model-bound, declarative, and reducer approaches on the same read model |
| Model-Bound Projections | Build read models with attributes — the default style |
| Declarative Projections | Build read models with the fluent IProjectionFor<T> API |
| Immediate Projections | Strong consistency — the read model updates before the append returns |
| Eventual Consistency | The default — how and when eventual projections catch up |
| Tagging Projections | Organize and tag projections |
| Appended event metadata filters | How tags and metadata correlate reducers and reactors that run alongside projections |
Once your read model exists, expose it to the frontend with a query — or see the whole loop in Build a full-stack feature.