# EventCategoryBinding

## Description

EventCategoryBinding is the seeded catalog that maps every supported `eventType` to exactly one `NotificationCategory`, declares whether the event is transactional, and lists the channels the dispatcher fans out to by default for that event. Each row carries `eventType`, `categoryId`, `transactional: boolean`, and `defaultChannels: NotificationChannelId[]`. The binding is the contract surface between the notification module and every emitter module: events whose `eventType` is absent from this catalog are rejected by the dispatcher with `CATEGORY_NOT_FOUND`.

The binding catalog is seeded by the host application — the module ships an empty `seed/` scaffold — and is immutable at runtime: adding a new emitter event is a host-app doc + seed change, not a runtime registration. Emitter modules MUST add a row before emitting. The set of supported events is therefore host-defined (e.g. an ERP host might seed announcement publish, task assigned, RFQ published / awarded, PO issued / changed, invoice submitted / approved, document expiring / expired, supplier lifecycle events); the Event Catalog section of the notification-delivery feature documents the required seed shape and example rows.

The `transactional` flag drives dispatch resolution: events bound to `transactional = true` rows bypass NotificationPreference at dispatch time and are always delivered on every channel listed in `defaultChannels`. The `defaultChannels` array is the channel set the dispatcher fans out to when the recipient has no preference rows for the resolved category — per-user opt-outs can suppress optional categories on individual channels, but the default set is always the starting point.

## Domain Model Definitions

### Model type

Standard

### Command Definitions

- No runtime commands — EventCategoryBinding rows are seeded by the host application and are not mutated through the command surface

### Query Definitions

- [getEventCategoryBinding](../query/GetEventCategoryBinding.md) - Resolve a single `eventType` to its `categoryId`, `transactional` flag, and `defaultChannels[]` for dispatcher use

### Models

- EventCategoryBinding

### Invariants

- `eventType` is the primary key and is unique across the catalog
- Every `eventType` is bound to exactly one `categoryId`; no fan-out across multiple categories is supported
- `defaultChannels[]` is non-empty and contains only `channelId` values registered in NotificationChannel
- `transactional = true` causes the dispatcher to bypass the preference filter and deliver on every channel in `defaultChannels`
- An `eventType` not present in the catalog is rejected by the dispatcher with `CATEGORY_NOT_FOUND` and persists no Notification rows
- The catalog is owned by the seed surface; rows cannot be created, updated, or deleted at runtime

### Relationships

- **References NotificationCategory as categoryId**: the bucket the eventType belongs to for preference resolution
- **References NotificationChannel as defaultChannels[]**: the channels the dispatcher fans out to by default; each entry must resolve to a row in the channel registry
- **Read by Notification at dispatch time**: the dispatcher resolves the eventType against this catalog as the very first step of dispatch
