This is a library to do CQRS/ES in Haskell. The core library eventsourcing
provides the abstractions and eventsourcing-postgresql
is the adaptor for
PostgreSQL. Other adaptors can be written and would be able to work together.
The stream is the most important concept of the library. It represents a sequence of ordered events that belongs together. In DDD (Domain-Driven Design) terminology, it corresponds to a single aggregate instance.
Streams that share a common structure can be brought together into stream families index by some stream identifier. In DDD parlance, this would be used for aggregate types. Streams in a stream family are pretty much independent from each other in the sense that events are only ordered within streams and event identifiers can be reused between streams without conflicts.
Projections should be as easy to write and maintain as possible. This is why,
with eventsourcing
, the user is expected to write simple functions with a type
of the following form.
event -> f a
For example, a simple aggregation function has the following type.
EventWithContext eventId metadata event -> State aggregate ()
For projections that populates SQL tables, the type would be as follows.
(streamId, EventWithContext eventId metadata event)
-> State st [TabularDataAction] ()
These functions can then be used by the library or an adapter to be run against some stream or stream family.
By keeping these functions simple, they can easily be tested and used in different contexts. Want to switch from one adapter to another or use in-memory store for your integration tests? No problem.
Sometimes, you want to transform some streams into other streams. You might want to migrate a stream family to get rid of obsolete streams or change the shape of some event. Maybe you want to publish events to a message broker but want to keep your internal (private) events separate from the (public) events that make their way to the broker.
Transformers are functions of the following form where inputEvent
is events
(with their context) coming from some stream or stream family and Transform
is
a monad in which you can push and merge events together to be sent downstream.
inputEvent -> Transform eventId event ()
-
Guided tutorial showcasing the core concepts.
-
Using aggregate stores to cache aggregates instead of going through events every time.
-
Projecting in memory from different streams into the same read model.
-
Writing tests that are efficient.
-
Optimistic concurrency control to write events without breaking consistency.
-
Using TabularDataAction to write backend-agnostic projections.
-
Migrating (and archiving) your events.
This list gives an overview of the concepts and terminology used in the library:
- stream
-
An (ordered) sequence of events belonging together forming a unit of consistency (aggregate instance in DDD.)
- stream family
-
A family of streams indexed by some stream identifier (aggregate type in DDD.)
- transformer
-
A function transforming a stream of events of type
a
into a stream of other events of typeb
. - aggregator
-
A function looping through events of a stream and accumulating an aggregate.
- aggregate store
-
An in-memory cache containing the aggregate returned by an aggregator indexed by the stream identifier.
- projection
-
A process going through events of a stream and doing something. More specifically, "effectful" projections can be used to project the stream of events into transactions that are sent to a database to create tables that can be queried later on.
- read model
-
Something such as table in the database or an aggregate store that can be queried. Read models are ways to read data without having to go through all the events of a stream over and over again.
- migration
-
A process going through all events of all streams in a stream family and migrating them — possibly compacting or deleting some of them — into a new stream family.
- task manager
-
A process using an aggregator of tasks (with or without a start time) for all streams in a stream family and executing them when they are due.
- tracking table
-
A table in a database used by a projection to keep track of what events have already been processed.
- adaptor
-
An integration between
eventsourcing
(the core library) and a back-end such as PostgreSQL.