One sequence of events can serve several readers. A support colleague needs a timeline. A customer needs a current status. An operations team needs a list of work requiring attention. Trying to make one screen, query and data shape serve all three often makes each one harder to use.

A projection turns source events into a view suited to a particular question. That makes its design partly an engineering task and partly a product task. The shape of the data should follow the decision someone needs to make.

Start with the screen’s promise

Consider a delivery dashboard. A customer-facing view may need the latest status and a readable explanation. An internal queue may need age, priority and the next permitted action. The same events can inform both, but the useful output is different.

Write the promise of each view in one sentence: “Show which deliveries need an operator’s attention.” That sentence helps decide which fields belong, which filters matter and which changes must update the view.

It also exposes a second question: how fresh must the answer be? A weekly report and a confirmation screen have different tolerances. “Eventually consistent” is an implementation description; it is not an adequate explanation to someone waiting for their payment to appear.

Make delay understandable

If a view updates asynchronously, decide what a person sees between the action and the update. A pending state, a last-updated label or a confirmation that processing has started may be appropriate. Avoid presenting stale information as a final failure.

The go-mink project includes inline, asynchronous and live projection mechanisms. Choosing among them still requires application-level judgment. A library’s available modes do not determine the right user experience or remove the need to measure processing delays.

Design for rebuilding

A projection that can be reconstructed is easier to change, but rebuilding deserves a plan. Decide whether the old view stays available, how the replacement is validated and when readers switch over. Large histories may make a rebuild operationally significant.

Test a short sequence containing a correction, a repeated delivery and an interruption. Check the final view, not just whether each handler returned successfully. Where duplicate delivery is possible, make the update logic safe to repeat.

A good read model is a small, explicit agreement with its reader: this is the question we answer, this is how current the answer is, and this is what happens while the system catches up. Treat that agreement with the same care as the event store beneath it.

Have a thought to add?Let’s keep talking