4.3 KiB
Main Flows
Producer -> Notification
sequenceDiagram
participant Producer
participant Stream as Redis Stream notification:intents
participant Consumer as Intent consumer
participant Notify as Notification Service
participant Redis
Producer->>Stream: XADD normalized intent
Consumer->>Stream: XREAD from stored offset
Consumer->>Notify: decode and validate envelope
alt malformed intent
Notify->>Redis: record malformed-intent entry
Consumer->>Redis: save stream offset
else duplicate with same normalized content
Notify->>Redis: load accepted notification
Consumer->>Redis: save stream offset
else idempotency conflict
Notify->>Redis: record malformed-intent entry
Consumer->>Redis: save stream offset
else new valid intent
Notify->>Redis: store notification, routes, and idempotency record
Consumer->>Redis: save stream offset
end
Duplicate handling is scoped by (producer, idempotency_key). request_id and
trace_id are observability-only metadata and do not participate in the
idempotency fingerprint.
User-Targeted Enrichment
sequenceDiagram
participant Consumer as Intent consumer
participant Notify as Notification Service
participant User as User Service
participant Redis
Consumer->>Notify: accepted user-targeted intent
loop each recipient_user_id
Notify->>User: GET /api/v1/internal/users/{user_id}
alt user exists
User-->>Notify: email + preferred_language
else subject_not_found
Notify->>Redis: record malformed intent recipient_not_found
Consumer->>Redis: save stream offset
else temporary failure
Notify-->>Consumer: service unavailable
Consumer-->>Consumer: stop before stream-offset advance
end
end
Notify->>Redis: persist enriched routes
User-targeted routes are enriched before durable route write. The currently
supported resolved locale is exactly en; unsupported or empty values fall
back to en.
Notification -> Gateway
sequenceDiagram
participant Push as Push publisher
participant Redis
participant Gateway as Edge Gateway
participant Client
Push->>Redis: load due push route
Push->>Redis: acquire temporary route lease
Push->>Push: encode FlatBuffers notification payload
Push->>Redis: XADD MAXLEN ~ gateway client-event stream
Push->>Redis: mark route published and remove from schedule
Gateway->>Redis: XREAD client-event stream
Gateway->>Gateway: sign outgoing GatewayEvent
Gateway-->>Client: fan out to all active user streams
Notification Service publishes user_id, event_type, event_id,
payload_bytes, and optional request_id / trace_id. It intentionally omits
device_session_id.
Notification -> Mail
sequenceDiagram
participant Email as Email publisher
participant Redis
participant Mail as Mail Service
Email->>Redis: load due email route
Email->>Redis: acquire temporary route lease
Email->>Email: encode template-mode command
Email->>Redis: XADD mail:delivery_commands
Email->>Redis: mark route published and remove from schedule
Mail->>Redis: XREAD mail:delivery_commands
Mail->>Mail: accept template delivery command
Notification-generated mail always uses source=notification,
payload_mode=template, and template_id == notification_type.
Auth-code mail is not part of this flow and remains a direct
Auth / Session Service -> Mail Service request.
Retry and Dead Letter
sequenceDiagram
participant Publisher
participant Redis
participant Downstream as Gateway or Mail Service
Publisher->>Redis: load due route
Publisher->>Redis: acquire temporary route lease
Publisher->>Downstream: append downstream stream entry
alt publication succeeds
Publisher->>Redis: mark published and remove schedule member
else retry budget remains
Publisher->>Redis: mark failed and schedule next attempt
else retry budget exhausted
Publisher->>Redis: mark dead_letter and write dead-letter entry
end
push and email retry independently. A dead-lettered route never rolls back
or invalidates a sibling route that already reached published.