Get read models
Goal: your events project into a read model — now you need it back out. One book, the whole catalog, a page for a grid, or a live stream that updates the UI as events arrive. IReadModels (on IEventStore.ReadModels) covers all of these; the choice is between strongly consistent reads computed from the event log and eventually consistent reads served from the materialized sink.
Get one instance
Section titled “Get one instance”GetInstanceById replays the key’s events through the projection or reducer on demand — strongly consistent, reflecting everything appended up to this moment:
using Cratis.Chronicle;using Cratis.Chronicle.Events;
public record ScenariosQueryBook(string Title, bool OnLoan);
public class ScenariosQueryBookService(IEventStore eventStore){ public Task<ScenariosQueryBook> GetBook(EventSourceId bookId) => eventStore.ReadModels.GetInstanceById<ScenariosQueryBook>(bookId.Value);}import io.cratis.chronicle.IEventStore
data class ScenariosQueryBook(val title: String, val onLoan: Boolean)
class ScenariosQueryBookService(private val store: IEventStore) { suspend fun getBook(bookId: String): ScenariosQueryBook? = store.readModels.getInstanceByKey(ScenariosQueryBook::class, bookId)}import io.cratis.chronicle.IEventStore;import kotlin.jvm.JvmClassMappingKt;import kotlinx.coroutines.BuildersKt;import kotlin.coroutines.EmptyCoroutineContext;import kotlin.coroutines.Continuation;
record ScenariosQueryBook(String title, boolean onLoan) {}
class ScenariosQueryBookService { private final IEventStore store;
ScenariosQueryBookService(IEventStore store) { this.store = store; }
ScenariosQueryBook getBook(String bookId) throws InterruptedException { return (ScenariosQueryBook) BuildersKt.runBlocking( EmptyCoroutineContext.INSTANCE, (scope, continuation) -> { @SuppressWarnings("unchecked") var readContinuation = (Continuation<? super ScenariosQueryBook>) continuation; return store.getReadModels().getInstanceByKey( JvmClassMappingKt.getKotlinClass(ScenariosQueryBook.class), bookId, readContinuation); }); }}defmodule MyApp.ReadModels.ScenariosQueryBook do defstruct [:title, :on_loan]end
defmodule MyApp.ScenariosQueryBookService do alias MyApp.ReadModels.ScenariosQueryBook
def get_book(book_id) do Chronicle.read_model(ScenariosQueryBook, book_id) endendimport { IEventStore } from '@cratis/chronicle';
class ScenariosQueryBook { constructor( readonly title: string, readonly onLoan: boolean ) {}}
class ScenariosQueryBookService { constructor(private readonly store: IEventStore) {}
async getBook(bookId: string): Promise<ScenariosQueryBook> { return this.store.readModels.getInstanceById(ScenariosQueryBook, bookId); }}ReadModelKey converts implicitly from string, Guid, and EventSourceId, so you can pass the id you already have.
Get all instances
Section titled “Get all instances”GetInstances rebuilds every instance by replaying the event log — strongly consistent, suited to reads where accuracy beats latency. The cost grows with history; filter the result with LINQ:
using Cratis.Chronicle;
public class ScenariosQueryOnLoanBooks(IEventStore eventStore){ public async Task<IEnumerable<ScenariosQueryBook>> GetOnLoan() { var books = await eventStore.ReadModels.GetInstances<ScenariosQueryBook>(); return books.Where(b => b.OnLoan); }}Page through materialized state
Section titled “Page through materialized state”For list views and grids, skip the replay: Materialized reads the instances the projection has already persisted to the sink — eventually consistent (typically milliseconds behind), fast regardless of history, and paged:
using Cratis.Chronicle;
public class ScenariosQueryBookPageService(IEventStore eventStore){ public Task<IEnumerable<ScenariosQueryBook>> GetPage() => eventStore.ReadModels.Materialized.GetInstances<ScenariosQueryBook>(skip: 0, take: 20);}Watch every change
Section titled “Watch every change”Watch<T>() returns an IObservable<ReadModelChangeset<T>> that emits whenever any instance of that read model type changes — the changeset carries the key, the new state, and whether the instance was removed:
using Cratis.Chronicle;
public class ScenariosQueryBookWatcher(IEventStore eventStore){ public IDisposable Watch() => eventStore.ReadModels.Watch<ScenariosQueryBook>() .Subscribe(changeset => { if (changeset.Removed || changeset.ReadModel is null) { return; }
Console.WriteLine($"{changeset.ModelKey}: on loan = {changeset.ReadModel.OnLoan}"); });}Dispose the subscription when you’re done. If you need explicit lifecycle control — for example awaiting the watcher’s Subscribed before producing events — use GetWatcherFor<Book>() instead.
Observe a live page
Section titled “Observe a live page”Materialized.ObserveInstances pushes a fresh page snapshot whenever the stored data changes — a live dashboard or table with no polling:
using Cratis.Chronicle;
public class ScenariosQueryLiveBookPage(IEventStore eventStore){ public IDisposable Subscribe(Action<IEnumerable<ScenariosQueryBook>> updateView) => eventStore.ReadModels.Materialized .ObserveInstances<ScenariosQueryBook>(take: 50) .Subscribe(books => updateView(books));}When a command appends BookBorrowed, the projection updates the materialized Book and every subscriber receives the new page. The brief gap between append and push is the normal eventual consistency window — usually imperceptible.
Pick by consistency
Section titled “Pick by consistency”| You need | Use | Consistency |
|---|---|---|
| One instance, exact current state | GetInstanceById<T>(key) | Strong |
| All instances, exact current state | GetInstances<T>() | Strong |
| A page for a list or grid | Materialized.GetInstances<T>(skip, take) | Eventual |
| To react to every change of a type | Watch<T>() | Pushed as changes apply |
| A live-updating page | Materialized.ObserveInstances<T>(skip, take) | Eventual, pushed |
See also
Section titled “See also”- Consistency models — the strong-vs-eventual trade-off in depth.
- Getting a single instance and Getting a collection of instances — caching, performance, and the non-generic forms.
- Materialized read models — pagination details and when to query the sink directly.
- Watching read models — changesets, filtering, and error handling.