Skip to content

Vertical Slices

This series of tutorials builds a Library system end to end, one behaviour at a time. Each tutorial corresponds to one of the four slice patterns from Event Modeling, and each one builds on the previous.

By the end you will have seen how every layer of the Cratis stack fits together: Chronicle event sourcing, Arc’s CQRS application model, and the purpose-built Components library.


Most software projects don’t fail because of bad code. They fail because the team built the wrong thing — or built the right thing but nobody agrees on what it actually does. A product owner, a developer, and a domain expert sit in a meeting, all walk out believing they understood each other, and three weeks later reality proves otherwise.

Event Modeling addresses this directly. It is a way to design and describe information systems using a shared timeline — a visual blueprint everyone on the team can read, from developers to domain experts to product owners. Unlike a traditional specification document, an event model is collaborative and alive. It uses only three building blocks and four patterns. You can explain the core concept in minutes; the rest you learn by doing.

Every Event Model is made from exactly three concepts:

Building BlockWhat it isExamples
EventsFacts — immutable records of things that have already happenedAuthorRegistered, BookBorrowed, LoanOverdue
CommandsIntentions — what a user (or system) is trying to do, which will cause an eventRegisterAuthor, BorrowBook, CancelReservation
Read ModelsOutputs — how the system informs users about what is going onThe author list, the inventory dashboard, the borrowing history

Put them together and you have a complete picture of any workflow: a command comes in, gets validated, an event is recorded, the read model is updated — and the user sees the result.

Three building blocks. Four ways to combine them. That is the entire vocabulary.

A user submits a command. It gets validated. An event is recorded.

RegisterAuthor fires → AuthorRegistered is stored. The intent is explicit, the outcome is captured. This is the most common pattern — the write side of your system.

In Cratis this is: a [Command] record with a Handle() method that returns a Chronicle [EventType], optionally enforced by a CommandValidator<T> or an IConstraint.

Events are projected into a read model that the UI displays.

An Author read model gets built from AuthorRegistered events. It is always up to date, and you can rebuild it from scratch at any point just by replaying the events. This is the read side — fast, purpose-built, and completely independent from the write side.

In Cratis this is: a [ReadModel] record decorated with [FromEvent<T>] attributes and a static query method that returns an ISubject<IEnumerable<T>> for real-time reactivity.

A processor watches a read model (think: a to-do list), picks up items, and fires a command to handle each one — entirely behind the scenes.

Sending an overdue notice when a loan passes its return date. Cancelling a reservation that was never collected. Triggering a payment. No human involved; the same building blocks, automated.

In Cratis this is: an IReactor that observes a Chronicle event stream and calls ICommandPipeline to fire commands back into your own system.

When an event comes from an external system — one you don’t own — you translate its language into yours. You don’t want raw payloads as domain events. You want BookInformationReceived and MemberImported — events that mean something in your own context.

In Cratis this is: an IReactor that listens for external events and fires commands in your own system, which in turn produce domain events with your own vocabulary.


PatternChronicleArcComponents
State Change[EventType] records stored in the event log[Command] + Handle(), CommandValidator<T>, IConstraintCommandDialog for the form UI
State ViewProjections ([FromEvent<T>], IProjectionFor<T>) building [ReadModel]IQueryFor<T> / IObservableQueryFor<T> generated proxiesDataPage for the listing UI
AutomationIReactor observing the event logICommandPipeline to fire commandsNo UI — runs in the background
TranslationIReactor on external event streamsICommandPipeline bridging to domain commandsNo UI — integration layer

The key insight: Chronicle stores the facts (events), Arc wires up the intent (commands) and the queries, Components renders the result. Each layer has one job and they compose cleanly.


All four tutorials build parts of a Library system with the following capabilities:

  • Authors — register and list authors
  • Members — register and list library members
  • Book Catalog — register books with ISBN and associate them with authors
  • Book Inventory — track how many copies are in stock
  • Reservations — reserve a book for a member, subject to availability
  • Lending — lend out a book and track return dates

The tutorials do not implement everything. Instead, each one picks the behaviour that best illustrates a single pattern, so the focus stays on the technique, not the domain complexity.


Work through these in order — each one builds on the context from the previous.

TutorialPatternWhat you build
State Change — Register an AuthorState ChangeRegisterAuthor command, AuthorRegistered event, AddAuthor React form using CommandDialog
State View — List AuthorsState ViewAuthor read model, projection from events, AllAuthors observable query, Authors listing page using DataPage
Automation — Cancel Expired ReservationsAutomationPendingReservations read model, CancelReservation reactor that fires automatically when a reservation expires
Translation — Import Members from HRTranslationReactor that listens for HRMemberCreated external events and fires RegisterMember in the library domain