Notification Indexes and Constraints
Summary
The schema declares 14 indexes — 4 unique and 10 non-unique — plus 7 primary keys. It declares no foreign keys, no check constraints and no default constraints.
Audience
Engineers, architects, QA, support and DevOps engineers.
Reference Content
The verified reference material for this topic is set out in the sections below.
Unique indexes
| Index | Table | Columns |
|---|---|---|
UX_Templates_Tenant_Key_Channel_Version | templates | tenant, key, channel, version |
UX_Recipients_Tenant_Type_Ref | recipients | tenant, type, external reference |
UX_Preferences_Scope | preferences | tenant, type, external reference, event type, channel |
UX_ProcessedEvents_EventId | idempotency ledger | event |
These four are the only structural data-integrity guarantees in the schema. Three of them include a nullable tenant column, so their behavior for global rows depends on the database's treatment of nulls in unique indexes — a characteristic of the platform, not of application code.
Non-unique indexes
| Index | Table | Columns |
|---|---|---|
IX_Templates_Key_Channel | templates | key, channel |
IX_Messages_Recipient_Status | messages | recipient, status |
IX_Messages_Tenant_Channel | messages | tenant, channel |
IX_Messages_EventId | messages | event |
IX_DeliveryAttempts_MessageId | attempts | message |
IX_Audit_EventId | audit | event |
IX_Audit_MessageId | audit | message |
IX_Audit_Correlation | audit | correlation |
IX_Audit_OccurredOn | audit | occurrence |
IX_ProcessedEvents_Correlation | idempotency ledger | correlation |
All index names are assigned explicitly with HasDatabaseName, so the database names are stable and do not depend on the tooling convention.
Distribution
| Table | Indexes |
|---|---|
| Audit log | 4 |
| Messages | 3 |
| Templates | 2 |
| Idempotency ledger | 2 |
| Recipients | 1 |
| Preferences | 1 |
| Delivery attempts | 1 |
The audit table is the most indexed; the tables that carry the highest write volume during a fan-out are also the ones with the most index maintenance cost.
Primary keys
Seven, one per table, each a single GUID column assigned client-side. No table uses a composite or database-generated key.
Query coverage gaps
Three read paths have no index leading with their filter column:
- The failed-queue endpoint filters messages on status alone; the only status index is led by recipient.
- The three status counts in the statistics endpoint filter on status alone.
- The inbox orders messages by creation time descending; no message index includes that column.
Absent constraint types
- No foreign keys. The migration issues no foreign-key statement; see relationships.
- No check constraints. Nothing constrains status, channel, stage or outcome columns to their defined constant sets, so any string within the length limit is storable.
- No default constraints. Defaults exist only as C# property initialisers.
- No filtered or partial indexes, no included columns, no covering index, no full-text index and no partitioning.
Classification
Implemented.
Requires confirmation
Whether check constraints on the enumerated string columns and covering indexes for the uncovered reads are intended requires confirmation.
Related Articles
See Also
Keywords
- Notification database
- Indexes & Constraints
- Draft database documentation
Source References
microservices/src/notification-service/Infrastructure/NotificationDbContext.csmicroservices/src/notification-service/Infrastructure/Migrations/20260705113059_InitialNotificationSchema.csmicroservices/src/notification-service/Infrastructure/Migrations/NotificationDbContextModelSnapshot.cs
Revision Information
- Status: Draft
- Last reviewed: 2026-07-21
- Review cycle: Quarterly