Common Mistakes
This guide describes common mistakes and misunderstandings that arise when working with EventSourcingDB. Each section highlights a specific problem, explains its background, and offers guidance on how to avoid or resolve it. For mistakes in event sourcing in general – such as vague event names, missing idempotency, or personal data in events – see Common Mistakes in Concepts.
Expecting to Delete Events Later#
EventSourcingDB does not support deletion. Once written, events remain part of the system permanently. If data must be removed, the only option is to migrate to a new instance using a filtered backup and restore process.
Designing systems under the assumption that events can be deleted leads to architectural problems. Deletion must be modeled explicitly – for example, by writing a user-erased or consent-withdrawn event, rather than trying to remove existing data.
Misunderstanding How Signatures WorkAdded in 1.1#
Some developers are surprised when signature verification fails, even though signing is enabled and the event hasn't been modified. This usually stems from a misunderstanding: Signatures in EventSourcingDB are not stored but generated dynamically using the currently active signing key. If the key changes, the same event yields a different signature. To verify an event after a key rotation, it must be fetched again.
This design avoids persisting cryptographic material and keeps the system stateless. However, it also means that signatures are only valid for short-term authenticity checks. For long-term verification or audit trails, rely on the event's stable hash, which remains unchanged regardless of key rotations.
A valid hash confirms the event content is unmodified. A valid signature proves the event was issued by a trusted instance. Only if both are valid can the event be considered intact and authentic. If a signature is missing, the event was either unsigned or the signature was stripped – and its authenticity should not be assumed.
Writing Without Preconditions#
Concurrent writes to the same subject can produce inconsistent or conflicting histories if no preconditions are used. Clients should use optimistic concurrency control to prevent race conditions.
Changed in 1.2EventSourcingDB supports four preconditions: isSubjectPristine, isSubjectPopulated, isSubjectOnEventId and isEventQlQueryTrue. Use them to assert the expected stream state before writing. This ensures that only one client can successfully write under a given condition.
Registering a Schema Too Early#
Registering a schema too early in the development process can cause long-term friction. Once a schema is registered in EventSourcingDB, it becomes immutable. If the event structure later changes – for example, because of new business requirements or adjustments in naming – the original schema cannot be altered or removed. The only option is to introduce a new event type with a different version.
This can clutter your event log with unnecessary versioning and introduce unnecessary complexity that could have been avoided. Before registering a schema, ensure that the event model has stabilized. Prototyping events without schema validation during the early phases can help explore the domain more freely. Once the structure and semantics are clear, schema enforcement becomes valuable for consistency and safety.
Registering a Schema Too Late#
If a schema is registered after many events have already been written, and those events do not match the schema, the registration will fail. EventSourcingDB enforces that all past events conform to the schema.
To avoid this, register schemas early – ideally before any production data is written. This ensures that data is validated consistently and that schema registration succeeds on first attempt.
Overusing Subject Recursion#
Recursive reads allow querying all sub-subjects, but this should be used carefully. Reading from / with recursion enabled loads all events – which may be millions – and is not suitable for all use cases.
Use recursive reads for projections or analytics, not for simple stream access. When working with a specific subject, disable recursion unless you need substreams.
Misusing fromLatestEvent#
The fromLatestEvent option is powerful but has limitations. It only works in chronological order and cannot be combined with lowerBound. It also assumes the event type you're referencing exists.
Using it without understanding these constraints can result in empty results or unexpected reads. Read the documentation carefully before using it in production.
Exposing EventSourcingDB Publicly#
EventSourcingDB is designed for use inside trusted environments. It uses a single shared API token and should not be exposed to the public internet.
Deploy it behind firewalls, within private networks, or using an API gateway. Never allow unauthenticated access or rely solely on obscurity.
Using latest as Docker Tag#
Using the latest tag when deploying EventSourcingDB is risky and not recommended. While it may seem convenient, it introduces a hidden dependency on the current state of the Docker registry. If a new version is pushed and latest points to it, your deployment may unexpectedly change – even if you made no modifications yourself.
This undermines reproducibility and stability, which are essential properties of event-sourced systems. Since EventSourcingDB guarantees deterministic behavior and strong consistency, upgrading the database should always be a conscious decision, not an accidental side effect of using an unpinned tag.
Always use a specific version tag (e.g. 1.2.0) when pulling or deploying the image. This ensures predictable behavior across environments, simplifies debugging, and avoids surprises during testing or recovery.
Assuming EventSourcingDB Provides Read Models#
EventSourcingDB stores events. It does not maintain projections, aggregates, or queryable views. These must be implemented in the application.
Expecting to "query" current state directly from the event store reflects a misunderstanding. Build and maintain read models explicitly through event processing logic.
Expecting Exactly Once Delivery#
EventSourcingDB provides at-least-once delivery for observers. It does not guarantee exactly once semantics. This responsibility lies with the application, which must implement deduplication and persistence mechanisms as needed.
Use idempotent handlers and persistent tracking to avoid duplicate side effects. Design for at-least-once delivery and promote resilience through clear contracts.
Using EventQL for Live Event Processing#
EventQL is designed for analyzing and transforming historical data. It is not a substitute for live observation or streaming systems.
Use the observation API when you need to react to events as they happen. Use EventQL for projections, exports, and query-based reporting.
Management UI Not Accessible in Browser#
If the management UI is not accessible in your browser but the API works correctly (e.g., curl requests succeed), check if you're using a browser-blocked port.
Browsers block certain ports for security reasons according to the WHATWG Fetch specification. Common examples include port 22 (SSH), port 25 (SMTP), port 6000 (X11), and port 6665 (IRC).
To solve this issue, restart EventSourcingDB using a different port, such as 3000, 4000, 8080, or 8443. For more information on changing ports, see Running EventSourcingDB.