Skip to content

Show data that updates live

Goal: a screen should reflect new data the moment it changes — a dashboard count, a list of open items — without the user refreshing or you writing polling code.

A read model can be served as an observable stream. Instead of returning a snapshot, the query returns an ISubject<T> that pushes a new value whenever the underlying read model changes:

[ReadModel]
[FromEvent<BookBorrowed>]
[FromEvent<BookReturned>]
public record OnLoan([property: Key] BookId Id, string Title)
{
public static ISubject<IEnumerable<OnLoan>> AllOnLoan(IMongoCollection<OnLoan> collection) =>
collection.Observe();
}

Observe() turns the collection into a live subject. As events update the read model, subscribers receive the new set.

The generated proxy for an observable query exposes a .use() hook (and the Components data table that wraps it). It re-renders on every push — no polling:

const [onLoan] = AllOnLoan.use();
// onLoan.data is always the latest set

Or drop it straight into a live table — see Displaying data.

When a command appends BookBorrowed, the projection updates the OnLoan read model, and the observable pushes the new value to every subscriber. The brief gap between the append and the push is the normal eventual consistency window — usually imperceptible.