Skip to content

Patterns for Temporal Queries

This guide explores patterns for performing time-based queries in event-sourced systems using EventSourcingDB. Temporal queries are essential for understanding how the system has evolved, analyzing trends, answering retrospective questions, and reconstructing domain state at specific points in time.

Unlike traditional databases that store only the current state, event-sourced systems retain the entire history of what happened. This makes them ideal for time-based analysis, but also introduces unique challenges. Querying across time means working with sequences of events, and often requires reinterpreting domain logic in historical context.

Understanding Temporal Questions

Temporal queries take many forms. Some questions ask for a snapshot at a specific moment:

  • What did the data look like on January 1st?
  • Which users were active at 9:00 AM today?

Others require analyzing changes over time:

  • How many books were acquired each week?
  • When did the price of a product last change?

Still others involve periods and durations:

  • How long was a machine in error state?
  • Which subscriptions were active between two dates?

The key distinction in event-sourced systems is that answers are not stored — they are derived. To answer time-based questions, you must replay events and interpret them relative to the time window or historical condition you care about.

Projecting State at a Specific Point in Time

A common temporal query is reconstructing the state of a domain entity as it existed at a specific moment. In EventSourcingDB, this typically involves reading all events for a given subject in chronological order, stopping at the desired timestamp or event ID.

Clients can use the upperBound parameter when reading from a stream to limit the result to only the events that occurred before or at a certain point. The application logic then replays those events to compute the historical state.

This pattern is especially useful for audits, debugging, or retrospective analysis. For example, to determine what a customer's account looked like before a dispute was filed, you can replay the stream up to the relevant event.

Building Read Models Over Time Windows

Temporal reporting often involves grouping events by time intervals. For instance, counting how many events of a certain type occurred per day, week, or month.

This can be done using EventQL's GROUP BY clause in combination with date functions such as YEAR, MONTH, DAY, or WEEKDAY. The result is a projection of time-bucketed aggregates that reveal trends over time.

To improve performance and reusability, such results are often persisted as read models. Instead of recalculating everything from scratch, the read model can be incrementally updated with new events, while historical results remain unchanged.

Detecting Transitions and Durations

Some queries are concerned with transitions — for example, when a device changed from one state to another — or with durations, such as how long a user session lasted.

These cases require detecting specific event patterns and computing the time difference between them. This logic typically lives outside EventSourcingDB, in the application layer. The database provides the raw events; interpretation is up to the consumer.

To detect such transitions, read the relevant stream and identify the pairs of events that represent start and end conditions. For duration, subtract the timestamps of the corresponding events. This allows tracking metrics such as uptime, usage duration, or time spent in a workflow stage.

Using Event Types and Subjects for Efficient Filtering

Efficient temporal queries rely on proper modeling. Events should have meaningful types that reflect domain changes, and subjects should be structured in a way that supports scoping and filtering.

If all state changes of interest are represented explicitly as events — such as subscription-activated or machine-entered-error-state — then querying becomes straightforward. If instead only low-level signals are recorded, the interpretation becomes more complex and less reliable.

Use subjects to isolate event streams, so that replaying or querying one domain concept does not require scanning unrelated events. This reduces both processing time and cognitive complexity.

Replaying with Time Constraints

In addition to filtering by event ID, EventSourcingDB supports reading events in chronological order. Clients can combine this with their own logic to stop processing once a given timestamp is exceeded. This avoids unnecessary work and enables time-bounded projections.

In some scenarios, it may be useful to perform multiple partial replays, each covering a defined time window. This helps parallelize processing and makes the results more explainable — for example, by computing monthly aggregates independently.

Practical Considerations

Time-based queries can be computationally expensive if they require processing large numbers of events. To manage this, consider building and maintaining incremental read models for frequently used queries. Use checkpoints to avoid replaying the same events multiple times. Consider also storing derived data if it is needed often or by multiple consumers.

Another consideration is time zone handling. Event timestamps are stored in UTC, and all temporal comparisons should be performed in UTC unless explicitly converted. Make sure your application logic respects this, especially when dealing with daily or weekly aggregations.

Finally, remember that events describe what happened — but not necessarily what was known at the time. If you need to analyze what was visible to a user or system at a given moment, make sure to apply the same rules, filters, and access logic that would have applied historically.

Designing for Temporal Queries

The power of event sourcing lies in its ability to answer questions that were never anticipated. Temporal queries are a prime example: they allow teams to explore the past, detect patterns, and derive insights without relying on precomputed state.

To make the most of this capability, design your events with time-based use in mind. Choose event types that clearly express domain transitions. Use subject hierarchies to scope queries. Record timestamps consistently. And build application logic that can safely replay and interpret history.

A well-modeled event stream is not just a log — it is a time machine. Use it to understand not only what changed, but when, how, and why.