effect-cqrs
Event sourcing and CQRS for Effect v4. Commands decide on events, events are the only source of truth, and read models are projections you can throw away and rebuild.
pnpm add effect-cqrs effectA trial port of moleculer-cqrs to Effect v4, so the API may change.
- #1AccountOpened{ owner: "Alice" }
- #2MoneyDeposited{ amount: 250 }+250
- #3MoneyWithdrawn{ amount: 40 }−40
- #4TransferSent{ to: "bob", amount: 60 }−60
- #5MoneyDeposited{ amount: 120 }+120
- #6TransferRefunded{ amount: 60, reason: "AccountNotOpen" }+60
{ status: "open", owner: "Alice", balance: 330, version: 6 }How a command flows
Command
A typed call, or JSON from a transport through
Commands guidedispatch. The payload is checked against its schema first.Aggregate
The service rebuilds the aggregate's state from its events, and the handler decides what happened, or fails with a typed error.
Aggregates guideEvent store
The events are appended, but only if nobody wrote to that aggregate since its state was loaded.
Storage guideView models
Each one reads the new events from its own checkpoint.
View models guidecaughtUpwaits until your own write is there.
A bank, running in your browser
Accounts are aggregates, three view models project them, and a saga moves money between them. The customer app on the left only shows read models, so you can watch them trail the store. The devtools on the right slow every stage down, pause and step commands, crash a projection, and replay the log.
Tests read like moleculer-cqrs
Fixture runs commands through the real service on an in-memory store, so validation and encoding are tested too. The chain is an Effect, which it.effect runs.
it.effect("deletes a todo", () => Fixture.make(Todo) .givenEvents([created]) .when("deleteTodo", {}) .expectEvent("TodoDeleted") .inspectState((state) => assert.isTrue(state.deleted)))Keep the log where it fits
Every store passes the same tests. The numbers come from one laptop, so read them as orders of magnitude.
- In memoryFor tests, examples and the browser.60,000 commands/s
- SQLiteOne process, one file, no server.0.1 ms a command
- PostgreSQLSeveral processes writing, positions in commit order.1 ms a command