Skip to content

Vertical slices

This series of tutorials builds a Library system end to end, one behavior 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. Each tutorial shows the backend in C#, Kotlin, Java and TypeScript. The TypeScript backend is a source preview; the backend differences below list what it and the JVM backends do differently.

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.

Every slice in an event model combines the three building blocks in one of four ways.

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: an Arc model-bound command whose handler returns a Chronicle event, optionally guarded by a command validator or a Chronicle constraint.

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 by replaying the events. This is the read side — fast, purpose-built, and completely independent from the write side.

In Cratis this is: a read model built by a projection or reducer, with a query that the frontend calls — observable where the backend supports it, so the UI updates in real time.

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. Canceling a reservation that was never collected. Triggering a payment. No human involved; the same building blocks, automated.

In Cratis this is: a Chronicle reactor that observes the event stream and sends commands through Arc’s command pipeline 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: a Chronicle reactor that listens for external events and fires commands in your own system, which in turn produce domain events with your own vocabulary.

PatternChronicleArcComponents
State ChangeEvent types stored in the event log, guarded by constraintsModel-bound command with a handler and validatorsCommandDialog for the form UI
State ViewProjections or reducers building a read modelQuery over the read model, with generated proxiesDataPage for the listing UI
AutomationReactor observing the event logCommand pipeline to fire commandsNo UI — runs in the background
TranslationReactor on external event streamsCommand pipeline bridging to domain commandsNo UI — integration layer

Chronicle stores the facts (events), Arc carries the intent (commands) and serves the queries, and Components renders the result. For the language-specific APIs, see Arc backends.

The tutorials teach one design in four backend languages. Where a backend cannot follow a step as written, the tutorial says what it does instead and links back to this table:

BehaviorC#Kotlin and JavaTypeScript (preview)
Author list (State View)Live: the query observes Chronicle’s MongoDB sinkLive: observes the first page of 50 materialized instances; needs Chronicle 19.22.2 or laterLive: ChronicleReadModels.observeAll
Commands called without a signed-in userThe commands declare no authorization rulesMarked @AllowAnonymous, because Arc on the JVM requires an authenticated caller by defaultThe commands declare no authorization rules
How Chronicle finds a reactor’s handler (Automation)Supported signatures and event parameter types on an IReactorThe event parameter type on a @Reactor classThe method name: the event class name with its first letter lowercased, on a @reactor() class
How a reactor runs commands (Automation)It calls the injected ICommandPipeline and throws on failureIt passes each command to ChronicleCommandSideEffectHandler and throws on failureIt returns the commands; Arc’s reactorCommandResultHandler runs them
Constraints in command specs (State Change)Enforced: the scenario runs an in-process Chronicle kernelNot enforced: the scenario’s event log is in memoryNot enforced: the scenario records events in memory

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 behavior 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
Register an authorState ChangeRegisterAuthor command, AuthorRegistered event, AddAuthor React form using CommandDialog
List authorsState ViewAuthor read model built from events, AllAuthors query (observable where the backend supports it), Authors listing page using DataPage
Cancel expired reservationsAutomationReservationDueForExpiry to-do list, passive PendingReservation read model, and a ReservationExpiryReactor that sends CancelExpiredReservation for each overdue reservation
Import members from HRTranslationMemberImportReactor that listens for HRMemberCreated external events and fires RegisterMember in the library domain