# What Is an Event in Magnitude's event-core? The Complete Technical Guide

> Understand what an event is in Magnitude's event-core. Learn about timestamped state changes, the BaseEvent interface, and ephemeral events for efficient data flow.

- Repository: [Magnitude/magnitude](https://github.com/magnitudedev/magnitude)
- Tags: deep-dive
- Published: 2026-09-05

---

**In Magnitude's event-core, an event is the fundamental, timestamped unit of state change that flows through the EventBus, must satisfy the `BaseEvent` interface with a mandatory `type` discriminator, and can optionally be marked as `ephemeral` to prevent persistence.**

An event in the context of the **magnitudedev/magnitude** repository represents the atomic payload that drives the entire reactive architecture. Every state transition, notification, or domain change is encapsulated as an object conforming to strict contracts defined in the core event bus implementation. Understanding this contract is essential for building projections, managing side effects, and ensuring durable state hydration across the framework.

## The BaseEvent Contract

All events in Magnitude must satisfy the `BaseEvent` interface defined in [`packages/event-core/src/core/event-bus-core.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/event-core/src/core/event-bus-core.ts). This contract serves as the foundation for type safety and runtime discrimination throughout the system.

The interface requires exactly one mandatory field and one optional flag:

```typescript
export interface BaseEvent {
  /** Discriminator used by the event bus and projections. */
  readonly type: string

  /**
   * Ephemeral events flow through the EventBus and trigger projections/signals,
   * but are **not** persisted to the EventSink and are **not** replayed during
   * hydration.
   */
  readonly ephemeral?: true
}

```

**Source:** [[`event-bus-core.ts`](https://github.com/magnitudedev/magnitude/blob/main/event-bus-core.ts)](https://github.com/magnitudedev/magnitude/blob/main/packages/event-core/src/core/event-bus-core.ts#L20-L35)

The **`type`** property acts as a discriminator that allows the `EventBusCore` to route events to specific projections and subscribers. The optional **`ephemeral`** flag determines durability: when set to `true`, the event triggers real-time projections but bypasses the `EventSink` entirely, meaning it will never be persisted or replayed during hydration.

Use ephemeral events only when the notification originates outside a projection (where signals cannot be used) and when the data is transient or derived—persisting it would be incorrect because the triggering code re-runs naturally on replay.

## Automatic Timestamp Enrichment

When an event is published, the `EventBusCore` automatically enriches it with a millisecond timestamp. This transformation yields the `Timestamped<E>` type, ensuring every consumer receives temporal context without manual instrumentation.

```typescript
export type Timestamped<E extends BaseEvent> = E & { readonly timestamp: number }

```

**Source:** [[`event-bus-core.ts`](https://github.com/magnitudedev/magnitude/blob/main/event-bus-core.ts)](https://github.com/magnitudedev/magnitude/blob/main/packages/event-core/src/core/event-bus-core.ts#L37-L39)

This automatic wrapping occurs inside the `publish` method before the event enters the processing queue. Consumers subscribing to the bus receive `Stream<Timestamped<E>>` values, allowing them to reason about event ordering and staleness.

## Event Lifecycle in event-core

The journey of an event through Magnitude's architecture follows a strict three-phase pipeline implemented in [`event-bus-core.ts`](https://github.com/magnitudedev/magnitude/blob/main/event-bus-core.ts) lines 92-151.

### Publishing Events

Developers interact with the `EventBusCoreService` API (declared lines 40-65) to emit events. The `publish` method accepts a raw `BaseEvent`, wraps it in the `Timestamped` type, and enqueues it for processing. This operation returns an `Effect` that succeeds when the event is safely queued.

### Internal Processing Pipeline

A single-consumer fiber dequeues each event sequentially to guarantee ordering. The pipeline executes:

1. **Projection Phase** – Runs all registered projections against the event
2. **Persistence Phase** – Forwards the event to the `EventSink` (unless marked `ephemeral`)
3. **Broadcast Phase** – Publishes the timestamped event via an internal `PubSub` to all subscribers

This architecture ensures that projections see events before subscribers, and durable storage occurs before broadcast confirmation.

### Subscribing to Events

Consumers obtain a `Stream` of `Timestamped<E>` values by subscribing to the bus. The API supports both broad subscriptions (all events) and filtered subscriptions (specific event types), enabling fine-grained reactive logic without performance penalties from filtering in user code.

## Working with Events in Practice

### Defining Custom Events

Extend `BaseEvent` to create domain-specific event types. Always use `readonly` modifiers for immutable event semantics:

```typescript
// my-event.ts
import type { BaseEvent } from '@magnitudedev/event-core/src/core/event-bus-core'

export interface UserCreated extends BaseEvent {
  readonly type: 'UserCreated'
  readonly userId: string
  readonly name: string
}

```

### Publishing Events

Use the `EventBusCoreTag` to access the service via Effect's dependency injection context, then call `publish` with your event payload:

```typescript
import { Effect } from 'effect'
import { EventBusCoreTag } from '@magnitudedev/event-core/src/core/event-bus-core'
import type { UserCreated } from './my-event'

const EventBus = EventBusCoreTag<UserCreated>()

const publishUser = (id: string, name: string) =>
  Effect.flatMap(
    EventBus,
    bus => bus.publish({ type: 'UserCreated', userId: id, name })
  )

```

### Subscribing to Event Streams

Create reactive pipelines by subscribing to specific event types. The subscription returns a `Stream` that can be consumed with standard Effect operators:

```typescript
import { Effect, Stream } from 'effect'
import { EventBusCoreTag } from '@magnitudedev/event-core/src/core/event-bus-core'

type AllEvents = UserCreated // | ...other events

const listenForUsers = Effect.flatMap(
  EventBusCoreTag<AllEvents>(),
  bus => bus.subscribeToTypes(['UserCreated'])
)

listenForUsers.pipe(
  Effect.flatMap(stream =>
    Stream.forEach(stream, ev => 
      Effect.sync(() => console.log('New user at', ev.timestamp, ':', ev.userId))
    )
  )
)

```

### Handling Ephemeral Events

For transient notifications that should not survive hydration, set the `ephemeral` flag to `true`:

```typescript
import { Effect } from 'effect'

const transientAlert = Effect.flatMap(
  EventBusCoreTag<AllEvents>(),
  bus =>
    bus.publish({ 
      type: 'CacheMissAlert', 
      ephemeral: true, 
      message: 'Data not found in cache' 
    })
)

```

This event triggers projections and subscriptions immediately but is excluded from the `EventSink` persistence layer, ensuring it never replays during application hydration.

## Summary

- **An event** in Magnitude is any object satisfying the `BaseEvent` interface with a mandatory `type` string discriminator.
- **Timestamping** is automatic: `EventBusCore` wraps all events in the `Timestamped<E>` type before processing.
- **Ephemeral events** bypass durable storage (the `EventSink`) and hydration replays by setting `ephemeral: true`.
- **Lifecycle** follows strict ordering: publish → enqueue → projection → persistence (if not ephemeral) → broadcast.
- **Source files** defining this behavior reside primarily in [`packages/event-core/src/core/event-bus-core.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/event-core/src/core/event-bus-core.ts).

## Frequently Asked Questions

### What is the difference between an event and a signal in Magnitude?

A **signal** is used when a notification originates from within a projection and does not require persistence, while an **event** (including ephemeral ones) flows through the `EventBus` and can trigger projections across the system. Use events when the notification crosses projection boundaries; use signals for internal projection communication.

### When should I use the ephemeral flag on an event?

Mark an event as **`ephemeral: true`** only when the notification is derived or transient and originates outside a projection (where signals cannot be used). If the event represents durable state change or could be triggered by replaying existing logic, omit the flag to allow persistence via the `EventSink`.

### How does timestamping affect event processing?

The `EventBusCore` automatically enriches every event with a `timestamp: number` field (Unix milliseconds) during the publish phase. This ensures all subscribers and projections receive consistent temporal metadata without requiring manual date instantiation in business logic.

### Where is the event persistence logic implemented?

Events are persisted through the **`EventSink`** interface, which accumulates non-ephemeral events awaiting durable storage. The routing decision occurs in the processing pipeline within [`event-bus-core.ts`](https://github.com/magnitudedev/magnitude/blob/main/event-bus-core.ts) (lines 92-151), where the bus checks the `ephemeral` flag before forwarding to the sink.