Skip to content

StudioNotificationService

Target

This page describes intended behaviour or structure. No implementation is claimed unless a section is explicitly marked Current implementation or Evidence.

Bounded context BC-29 Experience Composition
Platform / plane Factory Studio / Experience
Deployment granularity module of host · host StudioBff
Status target · ConnectSoft.Factory.Studio.Notifications
Built from connectsoft-microservice · libraries: connectsoft-extensions-saas-abstractions, connectsoft-extensions-messaging-model, connectsoft-extensions-persistence-model-nhibernate, connectsoft-extensions-service-model-signal-r
Decisions ADR-0014
Traceability FR-05 FR-10 FUX-02 FUX-04 NFR-01 NFR-10 XL-06

Purpose and responsibilities

StudioNotificationService turns platform events into something a person sees. Approvals waiting, alerts, releases ready, promotions and rollouts, incidents, learning proposals, exports and reconciliations arrive as events; the FanOut worker resolves who may see each subject through the owning platform's permission check — never from the event alone — applies the users' preferences and quiet hours (action-required and critical ignore them in-app), creates notifications with a summary and a reference, pushes them through the realtime gateway, and delivers email and chat through connectors with receipts. Digests group the rest hourly or daily. Alerts use the Notify gRPC call for a guaranteed in-app notice.

Context and boundaries

flowchart LR
    S["StudioNotificationService"]
    AlertRuleService["AlertRuleService"] -->|grpc| S
    StudioRealtimeGateway["StudioRealtimeGateway"] -->|event| S
    S -->|grpc| ControlPlaneApi["ControlPlaneApi"]
    S -->|grpc| ConnectorRegistryService["ConnectorRegistryService"]
    S -. "NotificationCreated" .-> E_NotificationCreated(("event"))
Hold "Alt" / "Option" to enable pan & zoom

Content is a summary with a reference; opening a notification fetches the subject through the BFF under the user's permissions at that moment, so a revoked permission cannot be bypassed by an old notification.

Domain model

Notification — One notification for one user: kind, subject reference, scope, importance, read and dismissed state, and the deliveries made per channel with receipts. (catalog entry)

Field Type Required Description
notificationId guid yes
tenantId string yes
userId string yes
kind enum:NotificationKind yes
subjectRef string yes
projectId string no
importance string yes info
summary string yes
readAt datetime no
dismissedAt datetime no
deliveries json yes
createdAt datetime yes

Invariants:

  • A notification is created only for users whose scope and role allow seeing the subject; the fan-out resolves recipients through the owning platform's permission check, never from the event alone.
  • Content is a summary with a reference; the Studio fetches the subject under the user's permissions when opened.

Events: NotificationCreated · Tables: Notifications · Repository: NotificationRepository

NotificationPreference — Per-user channel preferences, quiet hours and digest settings per notification kind. (catalog entry)

Field Type Required Description
userId string yes
tenantId string yes
channelsByKind json yes
quietHours json no
digest string yes none

Invariants:

  • Action-required and critical notifications ignore quiet hours for the in-app channel.

Events: — · Tables: NotificationPreferences · Repository: NotificationPreferenceRepository

erDiagram
    Notification {
        guid notificationId PK
        string tenantId
        string userId
        enum_NotificationKind kind
        string subjectRef
        string projectId
        string importance
        string summary
        datetime readAt
        datetime dismissedAt
        json deliveries
        datetime createdAt
    }
    NotificationPreference {
        string userId PK
        string tenantId
        json channelsByKind
        json quietHours
        string digest
    }
Hold "Alt" / "Option" to enable pan & zoom

Entities and value objects

None modelled

No entities or value objects are modelled for this item yet.

Data model and storage

Store Role Engine / profile Tables
Studio store primary azure-sql / nhibernate-sqlserver Notifications, NotificationPreferences
Table Owner Columns Indexes
dbo.Notifications Notification 12 1
dbo.NotificationPreferences NotificationPreference 5 0

Full DDL: appendix.

APIs

Method Path Operation Scope Side effects Idempotency Gateway Summary
GET /studio/notifications ListNotifications factory.studio.use read natural GW-02 The user's notifications with unread counts by importance.
POST /studio/notifications/{notificationId}/read MarkNotificationRead factory.studio.use shared-write natural GW-02 Mark read or dismissed.
PUT /studio/notifications/preferences SetNotificationPreferences factory.studio.use shared-write natural GW-02 Channels per kind, quiet hours, digest.
gRPC service Method Streaming Visibility Side effects Summary
Notifications Notify none internal shared-write Create notifications for the recipients of a subject (used by platforms that need a guaranteed in-app notice, for example alerts).

Messages

Event Topic Scope / v Aggregate Consumers Summary
NotificationCreated studio.notifications.v1.notification-created integration / 1 Notification StudioRealtimeGateway A notification exists for a user (the realtime gateway pushes it).

NotificationCreated payload:

Field Type Required Description
notificationId guid yes
tenantId string yes
userId string yes
kind enum:NotificationKind yes
importance string yes
Command Handled by Result events Summary
MarkNotificationRead Notification —
SetNotificationPreferences NotificationPreference —
Event Producer Handler Purpose
AlertFired AlertRuleService NotifyOperators Alerts reach operators in scope.
ReleaseReady ReleaseService NotifyProject A release is ready to promote.
PromotionCompleted EnvironmentPromotionService NotifyProject A promotion completed.
RolloutPlanned FleetRolloutService NotifyOperators A rollout awaits approval or started.
RolloutHalted FleetRolloutService NotifyOperators A rollout halted.
IncidentOpened FleetIncidentService NotifyOnCall An incident opened.
LearningProposalRaised FeedbackService NotifyMaintainers A proposal awaits a maintainer.
ReconciliationProposed RuntimeEnvironmentService NotifyOperators A reconciliation awaits an Operator.
ExportCompleted RetentionResidencyService NotifyRequester An export is ready.

Workers and jobs

Worker Trigger Output events Retry Idempotency key Summary
FanOut event NotificationCreated exponential eventId + userId Resolves recipients per event through the owning platform's permission check and the users' preferences, creates notifications and dispatches email and chat deliveries through connectors.
Digester schedule 0 * * * * — fixed userId + window Builds hourly or daily digests for users who chose them.

Actors and grains

None modelled

No grains are modelled for this item yet.

Sagas and long-running flows

None modelled

No sagas are modelled for this item yet.

flowchart LR
    EV[AlertFired · ReleaseReady · IncidentOpened · ProposalRaised · …] --> FO[FanOut]
    FO --> PERM[owning platform: who may see this subject in this scope?]
    PERM --> PREF[preferences · quiet hours · digest]
    PREF --> N["(Notification per user)"]
    N --> RT[StudioRealtimeGateway push]
    N --> CX[email · chat via connectors · receipts]
    N --> DG[Digester hourly · daily]
Hold "Alt" / "Option" to enable pan & zoom

Options and configuration

Section Key Type Default Reloadable Secret Summary
Studio:Notifications RetentionDays int 90 yes no Days notifications are kept.

Metrics and observability

Metric Instrument Unit Dimensions Summary
connectsoft.factory.studio.notifications counter {notification} tenant, kind, channel Notifications created by kind and channel.
connectsoft.factory.studio.notification_latency histogram s kind Event to in-app notification.

Security

Authentication oauth2-jwt
Scopes factory.studio.use (Own notifications and preferences.)
Roles Requester, Reviewer, AssetMaintainer, Operator, WorkspaceAdmin, PlatformAdmin, Observer
Tenant isolation per-request-claim
Data classification internal
Max side-effect class shared-write
Secrets —

Dependencies

Outbound

Target Kind Purpose Failure mode Required
ControlPlaneApi grpc Recipient resolution by scope and role. fail-fast yes
ConnectorRegistryService grpc Email and chat deliveries. degrade no

Inbound

Target Kind Purpose Failure mode Required
AlertRuleService grpc Guaranteed in-app alerts. — yes
StudioRealtimeGateway event Push of new notifications. — yes

Failure and recovery

Scenario Detection Recovery Data impact RTO (min)
Recipient resolution unavailable Control plane errors events queue; no notification is created without a permission check delayed notifications 30

NFR targets

Target Class value (pilot-interactive) Override
latency_p95_ms 2000 —
availability_pct 99.5 —
throughput_rps 50 —
rpo_minutes 15 —
rto_minutes 240 —
cost_class low —

Deployment

Granularity module of host · host StudioBff
Topologies saas-default, hybrid, on-prem
Runtime —; replicas —–—
Ports —

Current implementation

Nothing: the prototypes show a notification tray with static items.

Later capabilities

Notification rules authored by users (watch this run, this product); mobile push through a connector; team inboxes for on-call rotations.

Decisions and traceability

Family Ids
FR FR-05, FR-10
FUX FUX-02, FUX-04
NFR NFR-01, NFR-10
XL XL-06