feat: notification service

This commit is contained in:
Ilia Denisov
2026-04-22 08:49:45 +02:00
committed by GitHub
parent 5b7593e6f6
commit 32dc29359a
135 changed files with 21828 additions and 130 deletions
+64 -21
View File
@@ -218,7 +218,7 @@ Important architectural rules:
* public auth stays synchronous;
* `confirm-email-code` returns a ready `device_session_id`;
* no async “pending session provisioning” stage exists;
* no async “pending session provisioning” step exists;
* session source of truth and gateway-facing projection remain separate;
* active-session limits are configuration-driven;
* `send-email-code` stays success-shaped for existing, new, blocked, and throttled email flows.
@@ -291,7 +291,12 @@ Transport rules:
* `Auth / Session Service -> Mail Service` uses the dedicated synchronous
trusted internal REST contract `POST /api/v1/internal/login-code-deliveries`;
* `Notification Service -> Mail Service` is an asynchronous internal command
flow carried through the event bus or an equivalent queue-backed handoff.
flow carried through dedicated queue-backed handoff after durable route
acceptance inside `Notification Service`.
This split is covered by integration tests: auth-code delivery bypasses
`Notification Service`, while notification-generated mail uses template-mode
commands whose `template_id` equals `notification_type`.
`Mail Service` may internally queue both flows.
Its trusted operator read and resend APIs are part of the v1 service surface,
@@ -353,7 +358,7 @@ It is the source of truth for:
* game records before and after runtime existence;
* public/private game type;
* owner of a private game;
* invitations and invite code lifecycle;
* user-bound invitations and invite lifecycle;
* applications and approvals;
* membership and roster;
* blocked/removed participants at platform level;
@@ -410,8 +415,9 @@ Public games:
Private games:
* can be created only by eligible paid users;
* visible only to their owner and to invited users who used an invite code and were accepted;
* joining uses invite code plus owner approval;
* visible only to their owner and to invited users whose invitation is bound
to a concrete `user_id` and later accepted;
* joining uses a user-bound invite plus owner approval;
* invite lifecycle belongs entirely to `Game Lobby`.
Private-party owners get a limited owner-admin capability set, not full system admin power.
@@ -527,20 +533,42 @@ It executes runtime jobs for `Game Lobby` and `Game Master`.
This is a hard invariant.
## 10. Notification Service
## 10. [Notification Service](notification/README.md)
`Notification Service` is the async delivery/orchestration layer for platform notifications.
It has a deliberately minimal role:
* consume domain/integration events from services;
* decide whether a given event should result in push, email, or both;
* render and route notification payloads;
* send push-targeted events toward gateway;
* send email-targeted asynchronous commands toward `Mail Service`.
* consume normalized notification intents from services through dedicated
Redis Stream `notification:intents`;
* validate idempotency and persist durable notification route state;
* enrich user-targeted routes with `email` and `preferred_language` from
`User Service`;
* decide whether a given notification type results in `push`, `email`, or
both;
* send user-targeted `push` events toward gateway by `user_id`;
* send non-auth email asynchronous commands toward `Mail Service`.
It is not a source of truth for user preferences in v1 unless a later feature requires it.
For user-targeted intents, upstream producers publish the concrete recipient
`user_id` values. `Notification Service` resolves user email and locale from
`User Service`, uses configured administrator email lists per
`notification_type` for admin-only notifications, keeps
`template_id == notification_type` for notification-generated email, and
treats private-game invite flows in v1 as user-bound by internal `user_id`.
Go producers use the shared `galaxy/notificationintent` module to build and
append compatible intents into `notification:intents`; a failed append is a
notification degradation signal and must not roll back already committed source
business state.
Acceptance of a user-targeted notification intent is complete only after every
published recipient `user_id` resolves through `User Service`; unresolved user
ids are treated as producer input defects and are recorded as malformed
notification intents rather than deferred publication failures.
User-facing notifications use `push+email` unless a type explicitly opts out of
one channel. Administrator-facing notifications are `email`-only in v1.
All platform notifications except auth-code delivery flow through this service, including:
* game lifecycle notifications;
@@ -548,6 +576,13 @@ All platform notifications except auth-code delivery flow through this service,
* new turn notifications;
* operational/admin notifications where appropriate.
The current process surface exposes only one private probe HTTP listener with
`GET /healthz` and `GET /readyz`; that probe surface is documented in
[`notification/openapi.yaml`](notification/openapi.yaml). The canonical
notification-intent stream contract remains
[`notification/api/intents-asyncapi.yaml`](notification/api/intents-asyncapi.yaml).
It does not expose an operator REST API.
## 11. Billing Service (future)
`Billing Service` is not part of the first implementation wave.
@@ -611,6 +646,12 @@ The platform uses one simple rule:
* `Lobby -> Runtime Manager` runtime jobs;
* `Game Master -> Runtime Manager` runtime jobs;
* all event-bus propagation;
* `Game Master -> Notification Service` notification intents through
`notification:intents`;
* `Game Lobby -> Notification Service` notification intents through
`notification:intents`;
* `Geo Profile Service -> Notification Service` notification intents through
`notification:intents`;
* `Notification Service -> Gateway`;
* `Notification Service -> Mail Service`;
* geo auxiliary ingest from gateway to geo service;
@@ -626,13 +667,14 @@ The main example is `Lobby -> Game Master`:
## Redis as Data and Event Infrastructure
Redis is the first-stage shared infrastructure for:
Redis is the initial shared infrastructure for:
* main persistent data of services where no SQL backend is yet introduced;
* gateway session cache backing data;
* replay reservation store for gateway;
* session lifecycle projection;
* internal event bus using Redis Streams;
* notification-intent ingress through `notification:intents`;
* notification fan-out;
* runtime job completion events;
* lobby/game-master propagation events;
@@ -640,7 +682,7 @@ Redis is the first-stage shared infrastructure for:
Redis Streams are therefore the platform event bus in v1.
This is an accepted trade-off for simpler early-stage infrastructure.
This is an accepted trade-off for simpler early infrastructure.
Service boundaries must still stay storage-agnostic where future SQL migration is expected, especially in `Auth / Session Service`.
## Main End-to-End Flows
@@ -791,7 +833,7 @@ sequenceDiagram
Engine-->>GM: new turn result / maybe finished
GM->>GM: update current_turn and runtime state
GM->>Lobby: sync runtime snapshot
GM->>Notify: publish new-turn event
GM->>Notify: publish new-turn intent
Notify->>Gateway: client-facing push events
else generation failed
Engine-->>GM: error / timeout
@@ -820,7 +862,7 @@ sequenceDiagram
GM->>GM: update runtime state
GM->>Lobby: mark platform game finished
Lobby->>Lobby: finalize game record
GM->>Notify: publish finish event
GM->>Notify: publish game-finished intent
Notify->>Gateway: push user-facing/platform events
```
@@ -923,7 +965,7 @@ Uses the normal authenticated client protocol, not the separate system admin UI.
Allowed owner-admin actions are limited to the owners own private games and include at least:
* initiate enrollment;
* distribute invite codes outside the system;
* create and manage user-bound invites inside the system;
* approve/reject applicants;
* start game after enrollment;
* force next turn while running;
@@ -961,11 +1003,12 @@ Recommended order for implementation is:
3. **User Service** (implemented)
Regular-user identity, profile/settings, tariffs/entitlements, user limits, sanctions, and current `declared_country`.
4. **Mail Service**
Internal email delivery for auth codes first, later for platform notifications.
4. **Mail Service** (implemented)
Internal email delivery for auth codes and platform notification mail.
5. **Notification Service**
Unified async delivery of push and non-auth email notifications.
5. **Notification Service** (implemented)
Unified async delivery of push and non-auth email notifications, with
real Gateway and Mail Service boundary coverage.
6. **Game Lobby Service**
Platform game records, membership, invites, applications, approvals, schedules, user-facing lists, pre-start lifecycle.
@@ -983,6 +1026,6 @@ Recommended order for implementation is:
Auxiliary geo aggregation, review recommendation, suspicious-session blocking, declared-country workflow.
11. **Billing Service**
Late-stage payment and subscription source feeding entitlements into `User Service`.
Future payment and subscription source feeding entitlements into `User Service`.
This order gives the platform a usable public perimeter first, then identity/auth, then core gameplay lifecycle, then runtime orchestration, and only afterward secondary auxiliary services.