Skip to content

CRUD, EF Core, and Chronicle

If your instinct is to add a table, map an entity, and call SaveChanges(), you already have useful muscle memory. This page maps the CRUD/EF Core model onto Chronicle so the differences are explicit.

In CRUD you store the current state and overwrite it; in Chronicle you store what happened and derive the current state from those facts. The current state still exists — it’s a read model — you just build it from events instead of editing it in place.

You know (CRUD / EF Core)In Chronicle
A table / entityAn event source and its stream of events
INSERT a rowAppend a “created” event
UPDATE a columnAppend an event describing what changed (e.g. AddressChanged)
DELETE a rowAppend a “removed/closed” event — history is never erased
DbContext.SaveChanges()EventLog.Append(...)
SELECT / LINQ queryA query over a read model built by a projection
A computed/denormalized viewA purpose-built read model — make as many as you need
ALTER TABLE / EF migrationEvent type migration + replay the projection
Optimistic concurrency tokenConstraints and the event stream’s ordering
  • You still use C# records and dependency injection.
  • You still query data and render it — the read side looks like querying a collection.
  • You can still keep a relational/document database for the genuinely-CRUD parts of your app; Chronicle doesn’t demand all-or-nothing.
  • You model verbs, not just nouns. Instead of one mutable Customer row, you record CustomerRegistered, AddressChanged, AccountClosed. Each is an immutable fact. This is the part that feels new — and it’s where the value (audit, history, replay) comes from.
  • Reads are eventually consistent. A projection updates the read model after the event is appended, so a read immediately after a write may lag by a moment. Usually fine; occasionally something to design around. See Read Models.
  • You don’t write update statements. A projection declares how events map onto a read model; Chronicle keeps it current. No UPDATE, no merge logic.
  • You don’t delete history. “Delete” becomes an event. For real erasure obligations (GDPR), see Compliance.

CRUD: update a row, then read it back.

// EF Core
customer.Address = newAddress;
await db.SaveChangesAsync();
var current = await db.Customers.FindAsync(id);

Chronicle: append the fact; the read model reflects it.

await eventStore.EventLog.Append(customerId, new AddressChanged(newAddress));
// A projection updates the `Customer` read model; query it like any collection.

Read When to use event sourcing for an honest take — there are domains where plain CRUD is the right answer, and that’s fine.

  • Get started — scaffold and run in minutes.
  • Tutorial — build a small system from events up.