9.7 KiB
Notification Service Implementation Plan
This plan has been already implemented and stays here for historical reasons.
It should NOT be threated as source of truth for service functionality.
Summary
This plan builds Notification Service as a durable asynchronous orchestration
service between domain producers, Gateway, Mail Service, and User Service.
The implementation must keep business-audience resolution in the producer,
contact enrichment in Notification Service, client push delivery in
Gateway, and email execution in Mail Service.
Global Rules
- Keep
Notification Serviceorchestration-only. - Preserve direct auth-code email flow from
Auth / Session ServicetoMail Service. - Use one dedicated Redis Stream for normalized notification intents.
- Keep route retries independent per channel.
- Do not make notification delivery a correctness dependency for gameplay or geo review state.
- Keep user-facing push payloads lightweight.
Stage 01. Freeze Vocabulary And Cross-Service Ownership
Status: implemented.
Note:
- Later-stage artifacts may already exist in the repository as draft or pre-staged documentation.
- Their presence does not mark the corresponding later stages as implemented.
Goal:
- remove ambiguity before runtime work starts
Tasks:
- Freeze
notification:intentsas the dedicated ingress stream. - Freeze that producers publish concrete
recipient_user_idvalues for user-targeted intents. - Freeze that
Notification Serviceresolves user email and locale fromUser Service. - Freeze that admin-only notifications use type-specific configured email lists.
- Freeze that
template_id == notification_type. - Freeze that private-game invites in v1 are user-bound by internal
user_id.
Exit criteria:
ARCHITECTURE.md,TESTING.md, and service READMEs no longer contradict the agreed notification model
Stage 02. Define The Intent Contract
Status: implemented.
Goal:
- publish one stable producer-to-notification contract
Tasks:
- Add
notification/api/intents-asyncapi.yaml. - Freeze envelope fields:
notification_typeproduceraudience_kindrecipient_user_ids_jsonidempotency_keyoccurred_at_msrequest_idtrace_idpayload_json
- Freeze duplicate and conflict rules on
(producer, idempotency_key). - Freeze
audience_kind=user|admin_email.
Exit criteria:
- every producer can publish normalized intents without service-specific side agreements
Stage 03. Freeze The Notification Catalog
Status: implemented.
Goal:
- turn product decisions into one exact type catalog
Tasks:
- Freeze v1 types and channel matrix.
- Freeze which types are user-targeted versus admin-only.
- Freeze that
lobby.application.submittedis user-targeted for private games and admin-email-only for public games. - Freeze that
lobby.invite.revokedproduces no notification. - Freeze payload requirements per type.
Exit criteria:
- no notification type remains partially specified
Stage 04. Define Push Payload Schemas
Status: implemented.
Goal:
- freeze lightweight client-facing payloads
Tasks:
- Add
pkg/schema/fbs/notification.fbs. - Define one table per user-facing push type.
- Generate Go bindings under
pkg/schema/fbs/notification. - Document the mapping from
notification_typeto FlatBuffers table.
Exit criteria:
Gatewayand future client code have one stable schema file for user-facing notification payloads
Stage 05. Freeze Mail Template Contracts
Status: implemented.
Goal:
- make email handoff deterministic
Tasks:
- Freeze
payload_mode=templatefor notification-generated email. - Add initial
entemplates for all supported email types inmail/templates/<template_id>/en. - Update
maildocumentation so notification template IDs align withnotification_type. - Keep
Auth / Session Serviceauth-code mail unchanged.
Exit criteria:
- every supported email notification type has a documented template directory
Stage 06. Define Redis State And Retry Model
Status: implemented.
Goal:
- freeze durable service-local storage before runtime code
Tasks:
- Define
notification_record,notification_route,notification_idempotency_record,notification_dead_letter_entry, and malformed-intent storage. - Freeze Redis keys and schedule structures.
- Freeze route status vocabulary:
pendingpublishedfaileddead_letterskipped
- Freeze retry budgets:
push=3email=7
Exit criteria:
- the runtime can restart without losing accepted-or-retryable work
Stage 07. Build The Runnable Service Skeleton
Status: implemented.
Goal:
- create the initial process shape
Tasks:
- Add
cmd/notification. - Add
internal/app,internal/config,internal/api,internal/service, andinternal/adapterspackages. - Wire Redis startup checks, graceful shutdown, logger setup, and telemetry.
- Do not add an operator REST API in v1.
Exit criteria:
- the process boots with Redis and configuration validation only
Stage 08. Implement Intent Acceptance And Idempotency
Status: implemented.
Goal:
- durably accept valid intents and reject invalid or conflicting duplicates
Tasks:
- Consume
notification:intents. - Validate the envelope and normalized payload.
- Persist idempotency records and accepted notification records.
- Record malformed intents separately.
- Materialize channel routes according to the type catalog and
audience_kind.
Exit criteria:
- valid intents are durable and replay-safe before downstream publication begins
Stage 09. Implement User Enrichment And Locale Resolution
Status: implemented.
Goal:
- make user-targeted routes self-sufficient for later publication
Tasks:
- Read users by
user_idfromUser Service. - Extract
emailandpreferred_language. - Apply
enfallback when locale is missing or unsupported. - Keep admin-email routes independent from
User Service.
Exit criteria:
- every user-targeted route can be published without additional producer input
Stage 10. Implement Push Publication
Status: implemented.
Goal:
- hand off user-facing notification events to
Gateway
Tasks:
- Encode the correct FlatBuffers table per
notification_type. - Publish client events into the configured
Gatewaystream withuser_idtargeting only. - Apply independent
pushretry policy and route-level dead-letter handling.
Exit criteria:
- user-targeted push notifications survive temporary
Gatewaystream failures
Stage 11. Implement Mail Publication
Status: implemented.
Goal:
- hand off non-auth email notifications to
Mail Service
Tasks:
- Build template-mode generic mail commands.
- Set
template_id == notification_type. - Pass through normalized template variables from
payload_json. - Apply independent
emailretry policy and route-level dead-letter handling.
Exit criteria:
- user and admin email notifications are durably handed off to
Mail Service
Stage 12. Integrate Producers
Status: implemented.
Note:
- Implemented as the shared Go producer contract module
galaxy/notificationintentbecauseGame LobbyandGeo Profile Servicecode modules are not present in this repository yet.
Goal:
- move upstream services onto the new notification contract
Tasks:
Game Masterpublishes:game.turn.readygame.finishedgame.generation_failed
Game Lobbypublishes:lobby.runtime_paused_after_startlobby.application.submittedlobby.membership.approvedlobby.membership.rejectedlobby.invite.createdlobby.invite.redeemedlobby.invite.expired
Geo Profile Servicepublishes:geo.review_recommended
- Update
Game Lobbyarchitecture and later implementation plan to use user-bound private invites byuser_id.
Exit criteria:
- producers no longer rely on ad hoc notification-side audience inference
Stage 13. Add Observability And Recovery Coverage
Status: implemented.
Goal:
- make the async runtime supportable in operations
Tasks:
- Add metrics for intake, duplicates, enrichment, publish attempts, retries, dead letters, and lag.
- Add structured logging fields shared across intake and route publishers.
- Document manual recovery steps for dead-letter inspection and replay.
Exit criteria:
- the runtime exposes enough signals to detect stuck, noisy, or broken delivery
Stage 14. Complete Test Coverage And Documentation Alignment
Status: implemented.
Goal:
- close the loop across service tests, boundary tests, and docs
Tasks:
- Add service tests for malformed intents, duplicates, locale fallback, retry budgets, and route isolation.
- Add inter-service tests with
Gateway,Mail Service,Game Master,Game Lobby, andGeo Profile Service. - Update
TESTING.md. - Update
ARCHITECTURE.md,mail/README.md,geoprofile/README.md, and gateway examples. - Verify docs still state that auth-code mail bypasses
Notification Service.
Exit criteria:
- the implementation and the cross-service documentation describe the same contracts
Final Acceptance Criteria
The implementation is complete only when all of the following hold:
- valid intents are consumed from
notification:intents - duplicates are idempotent and conflicting duplicates are rejected
- user enrichment resolves email and locale from
User Service pushandemailroutes are persisted and retried independently- route dead letters are isolated per channel and per recipient
Gatewayfan-out remains user-wide, not session-specificMail Servicereceives template-mode commands whose template IDs match notification types- admin notifications remain
email-only - auth-code email still bypasses
Notification Service