Notification Service (MILTON.NotificationService)
Overview
The Notification Service is a standalone ASP.NET Core microservice that bridges RabbitMQ messaging and SignalR real-time push for the MILTON platform. It consumes notification messages published by backend workers (DocumentGenerator, API) and broadcasts them to connected Blazor clients via a SignalR hub.
This service exists as a separate process so that:
- The API server is not burdened with long-lived WebSocket connections.
- SignalR can scale independently from the API and worker services.
- The YARP gateway terminates TLS and routes
/hubs/*traffic to this service.
Architecture
flowchart LR subgraph Producers DG["DocumentGenerator"] API["MILTON API"] end RMQ["RabbitMQ<br/>notifications queue"] subgraph NotificationService["MILTON.NotificationService"] WH["Wolverine Handlers<br/>(NotificationHandler)"] Hub["DocumentHub<br/>/hubs/document"] end subgraph Clients["Blazor WASM Clients"] DE["DocumentEditor"] DL["DocumentsList"] PD["ProjectDashboard"] end Producers -- "publish messages" --> RMQ RMQ -- "consume" --> WH WH -- "broadcast via<br/>IHubContext" --> Hub Hub -- "SignalR<br/>WebSocket" --> Clients
Technology
| Concern | Technology |
|---|---|
| Runtime | ASP.NET Core 10 |
| Messaging | Wolverine + RabbitMQ |
| Real-time push | SignalR |
| Orchestration | .NET Aspire |
Key Files
| File | Purpose |
|---|---|
Program.cs | Service bootstrap — configures SignalR, Wolverine RabbitMQ listener on "notifications" queue |
DocumentHub.cs | SignalR hub with document-level and project-level group management |
NotificationHandlers.cs | Wolverine message handlers that map each message type to a SignalR broadcast |
DocumentHub
The hub exposes four client-callable methods for group membership:
| Method | Group Name | Used By |
|---|---|---|
JoinDocumentGroup(Guid) | doc-{documentId} | DocumentEditor |
LeaveDocumentGroup(Guid) | doc-{documentId} | DocumentEditor |
JoinProjectGroup(int) | project-{projectId} | ProjectDashboard, DocumentsList |
LeaveProjectGroup(int) | project-{projectId} | ProjectDashboard, DocumentsList |
All server-to-client events are sent to both the document group and the project group, so list pages and detail editors both receive updates.
Handled Messages
Each Wolverine message is consumed from the "notifications" RabbitMQ queue and broadcast as a SignalR event:
| Wolverine Message | SignalR Event | Payload |
|---|---|---|
NotifyProgressMessage | "Progress" | DocumentProgressEvent |
NotifyCompletedMessage | "DocumentCompleted" | DocumentCompletedEvent |
NotifyErrorMessage | "Error" | DocumentErrorEvent |
NotifyDocumentUpdatedMessage | "DocumentUpdated" | DocumentUpdatedEvent |
NotifyBlockUpdatedMessage | "BlockUpdated" | BlockUpdatedEvent |
NotifyBlocksAddedMessage | "BlocksAdded" | BlocksAddedEvent |
NotifySectionsAddedMessage | "SectionsAdded" | SectionsAddedEvent |
NotifyBlockRemovedMessage | "BlockRemoved" | BlockRemovedEvent |
Message contracts are defined in MILTON.Shared/Messaging/Notifications/. Event records are defined in MILTON.Shared/DTOs/Notifications/.
Aspire Integration
In the AppHost, this service is wired as:
var notificationService = builder.AddProject<Projects.MILTON_NotificationService>("notificationservice")
.WithReference(rabbit)
.WaitFor(rabbit);The YARP gateway routes /hubs/{**remainder} to this service for TLS termination and unified entry point.
Related Documentation
- Push-Based Live Updates — full event flow from producer to client
- Client DocumentStateService — frontend SignalR integration
- Document Generator — primary event producer