Notification Service — Domain Model
Status: populated
Owner: Platform Engineering
Last updated: 2026-04-18
1. Aggregates
NotificationLog (root)
Immutable record of a delivery attempt.
| Field | Type | Notes |
|---|
notificationId | UUIDv4 | Identity |
tenantId | UUIDv4 | |
accountId | UUIDv4 | |
recipientId | string | userId or platform identifier |
recipientAddress | string | Email address or E.164 MSISDN; masked in logs |
channel | NotificationChannel | EMAIL, SMS |
category | NotificationCategory | e.g. ACCOUNT, BILLING, OPERATOR_ALERT, SYSTEM |
templateId | UUIDv4 | FK to notification_templates |
sourceEventType | string | e.g. billing.invoice.generated.v1 |
sourceEventId | string | From event header Nats-Msg-Id |
status | DeliveryStatus | PENDING, SENT, FAILED, SUPPRESSED |
providerMessageId | string | null | SendGrid message ID or sms-orchestrator messageId |
errorMessage | string | null | On FAILED |
attemptCount | number | 1..3 |
sentAt | Instant | null | |
createdAt | Instant | |
NotificationPreference (root)
Per-account opt-out settings.
| Field | Type | Notes |
|---|
preferenceId | UUIDv4 | |
accountId | UUIDv4 | |
category | NotificationCategory | |
channel | NotificationChannel | |
optedOut | boolean | |
updatedBy | string | userId |
updatedAt | Instant | |
NotificationTemplate (root)
Versioned template for a notification type + channel.
| Field | Type | Notes |
|---|
templateId | UUIDv4 | |
type | string | e.g. USER_REGISTERED, INVOICE_GENERATED, OPERATOR_DOWN |
channel | NotificationChannel | EMAIL or SMS |
subject | string | null | Email subject (Handlebars); null for SMS |
bodyHtml | string | Handlebars + Mjml source for email; Handlebars for SMS body |
bodyText | string | Plain-text fallback |
variablesSchema | JSON Schema | Validated at save time and at render time |
isActive | boolean | Only active templates are used |
version | number | Auto-increment on update |
updatedBy | string | Admin userId |
updatedAt | Instant | |
2. Value Objects
| VO | Invariant |
|---|
NotificationChannel | Enum: EMAIL, SMS |
NotificationCategory | Enum: ACCOUNT, BILLING, OPERATOR_ALERT, SYSTEM_SECURITY, SYSTEM_INFO |
DeliveryStatus | Enum: PENDING, SENT, FAILED, SUPPRESSED |
RecipientAddress | Non-empty; email regex for EMAIL channel; E.164 for SMS channel |
3. Domain Services
| Service | Purpose |
|---|
PreferenceResolver | Given (accountId, category, channel) → boolean opted-out |
TemplateRenderer | Renders Handlebars + Mjml template with variable map; validates against variablesSchema |
EmailDeliveryPort | SendGrid adapter |
SmsDeliveryPort | sms-orchestrator adapter |
NotificationDispatcher | Orchestrates preference check → render → deliver → log |
4. Domain Events
This service publishes no NATS events. All output is side-effect (email/SMS delivery + PG log).
5. Notification Type → Category → Opt-out Policy
| Notification Type | Category | Opt-out Allowed? |
|---|
USER_REGISTERED | ACCOUNT | No (security) |
INVOICE_GENERATED | BILLING | No (contractual) |
OPERATOR_DOWN | OPERATOR_ALERT | Yes (admin preference) |
SYSTEM_SECURITY_ALERT | SYSTEM_SECURITY | No (mandatory) |
SYSTEM_INFO_ALERT | SYSTEM_INFO | Yes |
6. Invariants
- A
NotificationLog row is created for every dispatch attempt, including suppressed ones.
SYSTEM_SECURITY category ignores notification_preferences opt-out — always delivered.
templateId must reference an active template at time of dispatch.
recipientAddress is never stored in plaintext in application logs (masked as ***@domain.tld or hash).