# Modeling Events

This guide explains how to **model events effectively** in an event-sourced system. It focuses on **naming, structure, semantics, and best practices** for designing events that are **expressive, stable, and aligned with domain behavior**. Good event modeling is essential for building systems that are **maintainable, auditable, and adaptable** over time.

Events are the **central element** in any event-sourced system. They are not just records of what happened – they are the **source of truth**. This means that designing them well is not just helpful, but **foundational**. Once written, events **remain in the system indefinitely**. They are **read, projected, observed, queried, and interpreted repeatedly**, possibly for years. **Poorly modeled events lead to long-term friction and ambiguity**, while **well-modeled events create clarity, reduce coupling, and support evolution**.

## Events Describe Facts in the Domain

Every event represents something that **has already happened**. This is a **key difference** from commands or requests, which express intent or future action. As such, events should always be phrased in the **past tense**. A good event does not just describe **what changed**, but **why the change occurred** and **what it means** in the domain.

For example, an event like `book-acquired` expresses more than just a change in state – it reflects a **decision**, an **action**, and a **new reality**. It tells a story: that someone or something acquired a book, which is now part of the library's inventory. This kind of **expressiveness** helps both humans and systems understand what the event represents and how it should be handled.

## Event Types: Expressive and Consistent

The `type` field of an event is **required and central**. It follows the **[CloudEvents](/concepts/cloudevents)** standard and must be **unique, stable, and meaningful**. A good event type is **structured, namespaced, and easy to interpret**.

The recommended format uses a **reversed domain name as namespace**, followed by a **descriptive, hyphen-separated event name in lowercase**. For example:

```
com.example.library.book-acquired
```

This naming scheme ensures **global uniqueness**, **prevents collisions across domains**, and provides a **consistent structure that tools can rely on**. The use of **past-tense verbs** (`acquired`, `borrowed`, `cancelled`) highlights that the event reflects something completed. Avoid **generic names** like `changed` or `updated`, as they offer little insight into domain meaning.

Importantly, the **verb should appear within the event name**, not at the beginning. Prefixing events with verbs (`create-book`, `add-user`) leads to **command-style naming** and **breaks the conceptual separation between intent and fact**. Instead, events should **read like domain phrases**: `book-acquired`, `user-added-to-group`.

## Subjects: Identifying the Stream

Every event belongs to a **specific stream**, identified by its `subject`. The subject is not just metadata – it **defines the context** in which the event occurred. In practice, subjects are **path-like identifiers** such as `/books/42` or `/users/17/orders/9001`. All events sharing the same subject belong to the **same stream** and are **ordered accordingly**.

When subjects are **hierarchical**, substreams like `/books/42/pages/3` are **logically related to their parents**. This structure is useful for **grouping and filtering**, but it must be used with care. **Excessively deep or overly fragmented subject hierarchies** can lead to complexity and confusion. Choose a structure that **aligns with your domain** and **remains stable over time**.

When modeling events, the subject should **point to the domain entity or aggregate instance** the event refers to. For example, a `book-borrowed` event might use the subject `/books/42`, and a `reader-registered` event might use `/readers/23`. This makes it **clear what the event is about** and **where it fits in the overall event stream**.

## Data Structure and Semantics

The `data` field of an event contains its **payload** – the **structured information** that describes what happened in detail. This is typically a **JSON object with domain-relevant properties**, such as book titles, user IDs, or timestamps. Avoid including **technical metadata or internal state** unless it is part of the domain story.

A well-modeled data object:

- **Reflects domain language**
- **Contains only what is needed** to understand the event
- **Is stable and versionable over time**

Prefer **flat, clear structures** over nested or opaque objects. Use **consistent naming conventions** and avoid leaking **implementation details** (e.g. internal flags, database IDs). The data should be **readable by both humans and machines**, even years later.

Consumers that handle **missing fields gracefully** make it easy to **evolve event structures carefully**.

> **Try it with EventSourcingDB**
>
> In **EventQL**, the query language of EventSourcingDB, **dot notation** accesses nested fields within `data`, and a field that does not exist resolves to `null` – see **[EventQL](/docs/eventsourcingdb/eventql)**.

## Schema Registration and Validation

To ensure **data consistency**, an event store can validate events against a **JSON schema** registered for their event type. Once a schema is registered, all past and future events of that type must **comply**. This enforcement **prevents accidental drift** and makes events **more robust over time**.

> **Try it with EventSourcingDB**
>
> EventSourcingDB supports registering JSON schemas for event types – see **[Registering Event Schemas](/learn/tutorials/first-steps-with-eventsourcingdb/registering-event-schemas)**.

If **schemas are immutable**, any **breaking change** (such as adding a required field or renaming a property) requires a **new event type**. This is one reason why including a **version number in the event type name** is recommended:

```
com.example.library.book-acquired.v1
com.example.library.book-acquired.v2
```

This convention allows **different versions of an event to coexist**, **simplifies migration**, and **enables smooth evolution** without modifying historical data.

## Modeling With the Future in Mind

Events are **permanent**. Once they are written, they are not changed. This makes it crucial to model them with **long-term use** in mind. Ask yourself:

- Will this **still make sense in five years**?
- Is the **meaning of this event clear** to someone unfamiliar with the system?
- Would I be able to **explain this to a domain expert**?

Events should be **expressive enough to support unknown future use cases**, such as **reporting, analytics, machine learning, or legal audits**. At the same time, they should **remain focused and minimal**, avoiding noise or unnecessary complexity.

A **good event tells a story**. It captures a **decision, a fact, or a transition** that matters in the domain. It is **written once, understood often**, and used in ways the original author might never have imagined. Designing such events requires **care** – but the payoff is a system that **remains stable, transparent, and valuable for years to come**.

## Capturing the Model in Writing

Once your events are well-modeled, **write them down as a model**, not just as code. A separate model gives you a **single place** to discuss naming, structure, and ownership with **domain experts** – without diving into implementation details.

> **Try it with ESDM**
>
> **[ESDM](https://esdm.io/)** – the **Event-Sourced Domain Modeling language** – captures events, their data shape, and the surrounding aggregates and commands as plain YAML. The included linter checks for **expressive naming**, **stable structure**, and **versioning** – exactly the qualities this guide recommends. From there, the path to **registering the events in EventSourcingDB** is short and mechanical.
