Contracts: Ensuring Data Integrity Across Boundaries
The Contracts module dictates the exact structural shape of data flowing into the Notification Service via RabbitMQ, and flowing out of it via SignalR WebSockets. It is split into two primary paradigms: Inbound Messages and Outbound Events.
Business Logic Intent
Because MILTON’s backend services do not share a monolithic “Domain Contracts” assembly (adhering to microservice best practices), the Notification Service must independently declare the structure of the messages it expects to receive. The primary intent here is Parity and Decoupling.
By leveraging Wolverine’s [MessageIdentity("...")] attribute, the NotificationService binds its local record classes to the exact byte-stream emitted by the API or DocumentGenerator, allowing type-safe deserialization without a DLL dependency.
Simultaneously, the outbound SignalR events are carefully shaped to match the React client’s exact expectations. In some cases, such as NotificationBlock and NotificationSection, the JsonPropertyName attributes are explicitly hardcoded to mimic the camelCase format of the API’s standard HTTP responses. This guarantees that when the React client receives a WebSocket update, it can drop the payload directly into its local TanStack cache without writing custom mapping logic.
Inbound Messages (NotificationMessages.cs)
These records represent the data structures consumed from the "notifications" RabbitMQ queue.
| Message Identity | C# Record | Trigger |
|---|---|---|
notify-progress | NotifyProgressMessage | Background worker finishes a single item (e.g., one block generated). |
notify-completed | NotifyCompletedMessage | Entire background saga completes successfully. |
notify-error | NotifyErrorMessage | Hard failure in processing. |
notify-document-updated | NotifyDocumentUpdatedMessage | High-level status transitions (e.g. Draft → Complete). |
notify-block-updated | NotifyBlockUpdatedMessage | AI completes generation of a specific content block. |
notify-blocks-added | NotifyBlocksAddedMessage | The structural generator spawns new child blocks dynamically. |
notify-sections-added | NotifySectionsAddedMessage | The structural generator spawns new sections dynamically. |
notify-block-removed | NotifyBlockRemovedMessage | The structural generator deletes an obsolete block. |
Outbound Events (NotificationEvents.cs)
These records represent the data structures broadcasted to the React client over the SignalR WebSocket connection.
When a message is received from Wolverine, a handler wraps it into an Event DTO, often appending an explicit timestamp (DateTime.UtcNow) to help the frontend order the updates.
Client Data Parity
The deeply nested complex types, such as
NotificationBlockandNotificationSection, use[JsonPropertyName("camelCase")]precisely to ensure that the JSON payload emitted by SignalR perfectly mirrors theContentBlockDtoandSectionDtoreturned by the standard MILTON API REST endpoints. This enforces a consistent data model across HTTP and WebSocket transports.