Event Definitions & Schemas
Relevant source files
The following files were used as context for generating this wiki page:
In the bubus ecosystem, events are the primary units of communication, encapsulated by the BaseEvent class. Every event carries metadata for orchestration—such as unique identifiers, parent tracking, and timeouts—alongside a custom data payload. Schemas are used to strictly define the shape of these events and the expected results from their handlers, ensuring type safety and facilitating cross-language compatibility between TypeScript and Python environments.
Sources: bubus-ts/src/base_event.ts:1-25, bubus-ts/tests/ts_to_python_roundtrip.test.ts:1-15
The BaseEvent Structure
All events inherit from a standard base schema that tracks the lifecycle, identity, and relationships of the event as it moves through the bus.
Core Metadata Fields
The BaseEvent class includes mandatory fields for auditing and routing:
| Field | Type | Description |
|---|---|---|
event_id | uuidv7 | Unique identifier used for deduplication and tracking. |
event_type | string | The class name or type discriminator of the event. |
event_created_at | ISO8601 | Timestamp of event instantiation. |
event_status | enum | Current state: pending, started, or completed. |
event_parent_id | uuid | null | Link to the event that triggered this one, enabling tree tracking. |
event_path | string[] | History of bus labels the event has traversed to prevent circular routing. |
Sources: bubus-ts/src/base_event.ts:98-125, bubus-ts/src/types.ts:3-10
Event Lifecycle Flow
The following diagram illustrates how an event transitions between statuses as it is processed by the EventBus.
The transition to completed resolves the internal done() signal, allowing callers to await the result.
Sources: bubus-ts/src/base_event.ts:476-530, TODO_ts.md:15-20
Defining Events with Schemas
Events are typically defined using the extend method, which combines the base metadata with custom field definitions. In TypeScript, this is powered by Zod, while the Python implementation uses Pydantic.
Custom Data and Results
When extending BaseEvent, developers can provide:
- Event Shape: A Zod/Pydantic schema defining the input data.
- Result Type: An
event_result_typeschema that validates values returned by handlers.
const RegisterUserEvent = BaseEvent.extend('RegisterUserEvent', {
email: z.string().email(),
plan: z.enum(['free', 'pro']),
event_result_type: z.object({
user_id: z.string(),
welcome_email_sent: z.boolean(),
}),
})
Sources: bubus-ts/examples/simple.ts:7-16, bubus-ts/src/base_event.ts:173-195
Schema Normalization
bubus supports shorthand constructors for common result types. These are normalized into full Zod schemas during event instantiation.
| Shorthand | Zod Equivalent |
|---|---|
String | z.string() |
Number | z.number() |
Boolean | z.boolean() |
Array | z.array(z.unknown()) |
Object | z.record(z.string(), z.unknown()) |
Sources: bubus-ts/src/types.ts:73-90, bubus-ts/tests/typed_results.test.ts:70-85
Cross-Language Compatibility
A core feature of the schema system is the ability to serialize events into JSON and rehydrate them in different runtimes without losing type semantics.
JSON Schema Roundtripping
When an event is serialized via toJSON(), the event_result_type is converted into a standard JSON Schema. This allows a Python bubus bus to validate a result against a schema originally defined in TypeScript.
The ts_to_python_roundtrip.test.ts file verifies that complex objects, including nested arrays and records, maintain their validation logic across this bridge.
Sources: bubus-ts/tests/ts_to_python_roundtrip.test.ts:250-300, bubus-ts/src/types.ts:100-110
Database Representation
When bridged to PostgreSQL or SQLite, the event schema is flattened. Key metadata fields become indexed columns, while the remaining custom data is stored in a JSON column.
| Column | Data Source |
|---|---|
event_id | BaseEvent.event_id |
event_type | BaseEvent.event_type |
event_payload | Entire serialized event JSON |
event_* | Dynamically created columns for specific metadata fields |
Sources: bubus/bridge_postgres.py:100-120, bubus-ts/src/bridge_postgres.ts:130-145
Result Validation Logic
The event_result_type is enforced at runtime when a handler finishes execution. If a handler returns a value that does not match the schema, the EventResult status is set to error, and an EventHandlerResultSchemaError is raised.
Validation Sequence
- Handler completes and returns
raw_value. EventResultchecks ifevent.event_result_typeexists.- If present,
safeParse()is called. - On success,
result.resultis set to the parsed value. - On failure,
result.errorcaptures the validation details.
Sources: bubus-ts/tests/typed_results.test.ts:105-115, bubus-ts/src/base_event.ts:394-410
Conclusion
The event definition system in bubus provides a rigid structure for asynchronous communication while maintaining flexibility for custom payloads. By leveraging industry-standard schema libraries and serializing to JSON Schema, it ensures that event-driven architectures remain robust and debuggable across heterogeneous environments.
Sources: bubus-ts/examples/simple.ts:100-110, bubus-ts/src/base_event.ts:550-560