Files
galaxy-game/notification/PLAN.md
T
2026-04-22 08:49:45 +02:00

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 Service orchestration-only.
  • Preserve direct auth-code email flow from Auth / Session Service to Mail 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:intents as the dedicated ingress stream.
  • Freeze that producers publish concrete recipient_user_id values for user-targeted intents.
  • Freeze that Notification Service resolves user email and locale from User 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_type
    • producer
    • audience_kind
    • recipient_user_ids_json
    • idempotency_key
    • occurred_at_ms
    • request_id
    • trace_id
    • payload_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.submitted is user-targeted for private games and admin-email-only for public games.
  • Freeze that lobby.invite.revoked produces 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_type to FlatBuffers table.

Exit criteria:

  • Gateway and 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=template for notification-generated email.
  • Add initial en templates for all supported email types in mail/templates/<template_id>/en.
  • Update mail documentation so notification template IDs align with notification_type.
  • Keep Auth / Session Service auth-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:
    • pending
    • published
    • failed
    • dead_letter
    • skipped
  • Freeze retry budgets:
    • push=3
    • email=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, and internal/adapters packages.
  • 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_id from User Service.
  • Extract email and preferred_language.
  • Apply en fallback 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 Gateway stream with user_id targeting only.
  • Apply independent push retry policy and route-level dead-letter handling.

Exit criteria:

  • user-targeted push notifications survive temporary Gateway stream 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 email retry 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/notificationintent because Game Lobby and Geo Profile Service code modules are not present in this repository yet.

Goal:

  • move upstream services onto the new notification contract

Tasks:

  • Game Master publishes:
    • game.turn.ready
    • game.finished
    • game.generation_failed
  • Game Lobby publishes:
    • lobby.runtime_paused_after_start
    • lobby.application.submitted
    • lobby.membership.approved
    • lobby.membership.rejected
    • lobby.invite.created
    • lobby.invite.redeemed
    • lobby.invite.expired
  • Geo Profile Service publishes:
    • geo.review_recommended
  • Update Game Lobby architecture and later implementation plan to use user-bound private invites by user_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, and Geo 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
  • push and email routes are persisted and retried independently
  • route dead letters are isolated per channel and per recipient
  • Gateway fan-out remains user-wide, not session-specific
  • Mail Service receives template-mode commands whose template IDs match notification types
  • admin notifications remain email-only
  • auth-code email still bypasses Notification Service