Concepts
This page introduces the core vocabulary of iwentsourcing. Follow the links for the full API documentation on each topic.
Tenant
A tenant is the top-level isolation boundary. Every API call, stream instance, aggregation, and piece of state is scoped to a tenant string (e.g. 'acme'). The same manifesto can be deployed to multiple tenants — for example, to separate environments like dev, staging, and production — but their data is completely isolated.
Realm
A Realm is the authentication namespace. It defines which identity providers are trusted (JWT, Google OAuth, Microsoft OAuth, etc.) and groups streams and aggregations under a common auth config. A realm belongs to a single tenant. Every stream and aggregation belongs to exactly one realm, and an aggregation can only subscribe to events from streams within the same realm — this is a security boundary that cannot be crossed.
Event
An Event is an immutable, typed record of something that happened. Events have a string key (e.g. 'create-todo'), a typed payload, a unique id, and a createdAt timestamp. Events are the only source of truth — state is derived from them and can always be rebuilt by replaying the event log.
Stream
A Stream is an append-only log of events scoped to a single identity (determined by its pattern). Events pushed into a stream are processed by handle functions that update the stream's state. Each stream instance is independent.
Pattern
A Pattern separates a single stream or aggregation definition into multiple isolated instances. Each unique combination of resolved $(variable) placeholders produces an independent instance with its own event log and state. This isolates access (e.g. one instance per user) and enables parallel processing — workers handle different instances concurrently without contention. Variables come from auth token claims ($(auth.sub), $(auth.aud), $(auth.iss)) or explicit args ($(args.key)).
State
State is the queryable snapshot of what a stream or aggregation currently knows. It is derived from events by handle functions and stored as typed key-value tables with primary keys and optional secondary indexes. State is never the source of truth — it can always be rebuilt by replaying events.
Handle
A handle is a function registered for a specific event/output key on a stream or aggregation. It receives the event payload and a context object and applies changes to state. Handles are the only place where state is mutated.
Output
An output is similar to an event in shape — it has a key and a typed payload — but serves a different purpose. Outputs are produced inside a handle function (ctx.output(key, data)) in response to processing an event. Aggregations can subscribe to them via injectOutput, just like events. Unlike events, outputs are not stored in the event log and are therefore not replayed when rebuilding state. This makes them the right tool for triggering external effects — sending an email, dispatching an SMS, calling a webhook — that should happen exactly once at the time of processing, not on every replay.
Aggregation
An Aggregation is a cross-stream read model. It subscribes to events (or outputs) from one or more streams using injectEvent / injectOutput and builds its own state projection. Use aggregations for cross-stream projections with a different shape than a single stream provides.
Event Store
The Event Store is the append-only persistence layer for raw events. It stores every event pushed to a stream and exposes operations to push, get, and range-scan events. The event log is immutable — events are never modified once written.
Registry
The Registry stores the manifesto — locally (in-memory or on disk) or remotely (in the API database). It holds the full stream and aggregation definitions including their bundled runtime source code, which the workers execute when processing events. When an event arrives the system queries the registry to find the matching definition by (tenant, realm, pattern, version).
Manifesto
The Manifesto is the serialised snapshot of your stream, aggregation, and realm definitions at a point in time. It is produced by iwentsourcing build or iwentsourcing upload. The API uses it to register definitions and checks it for breaking schema changes before accepting an upload.
