Skip to main content

Notification Event Handlers

Summary

There is no per-event handler class. A single EventNotificationResolver maps every consumed event to zero or more notification intents through one switch expression, and the dispatcher executes them.

Audience

Engineers, architects, QA, support and implementation partners.

Reference Content

The verified reference material for this topic is set out in the sections below.

Handler shape

The resolver is registered as a singleton and is stateless apart from its bound options. It exposes one method taking an envelope and returning a read-only list of intents.

A notification intent carries four things: the template key, the resolved recipient, the channel list and the template variables. It carries no delivery instruction — how an intent becomes a message is entirely the dispatcher's concern.

Dispatch mapping

One switch expression covers all 42 supported types. Handlers are grouped by shape rather than one-per-event:

Handler groupEventsForm
Individually written10One private method each
Asset lifecycle6One method parameterised by event type and template key
Performance7One method parameterised by template key and audience
Training9One method parameterised by event type and template key
Helpdesk10One method parameterised by audience

Any event type not matched by the switch returns an empty intent array, which the dispatcher records as a no-recipient outcome. Unknown events are absorbed, never thrown.

Variable extraction

Each handler deserializes the payload into its contract or local record, guards on a key identifier, and builds a string-to-string variable dictionary for template rendering. Missing values are coalesced to empty strings, and dates are formatted through a shared helper, so a sparse payload renders with blanks rather than failing.

A handler returns null when the payload cannot be parsed or its key identifier is empty; null becomes an empty intent list.

Channel selection

Each handler names a default channel set — in-app and email for all but one event, in-app only for the payroll-frozen event. Every default passes through a resolution helper that first consults an optional per-event configuration map.

When configuration supplies a non-empty channel array for the event type, that array replaces the default and each entry is normalised to canonical channel casing. The lookup is case-insensitive on the event type. This is the documented mechanism for adding or removing a channel for a pilot without a code change.

Multi-recipient handling

Only the Helpdesk group can produce more than one intent from one event. Its audience selector resolves requester, assignee, an admin fallback, or a combination, filters out nulls, and de-duplicates by recipient type and external reference before producing one intent per remaining recipient.

Execution

The dispatcher iterates intents, and for each iterates its distinct normalised channels, applying preference evaluation, address resolution and template resolution per channel before creating a message and delivering it. A channel with no resolvable address or a missing template is skipped and audited rather than failing the event.

Verified absence

There is no handler interface, no handler registry, no per-event handler class, no pipeline behavior, no middleware and no mediator. Adding an event means adding a switch case and a seeded template — which is exactly what the source comment states.

Classification

Implemented.

Requires confirmation

Whether the single-resolver shape is intended to scale past its current size requires confirmation.

See Also

Keywords

  • Notification database
  • Handlers
  • Draft database documentation

Source References

  • microservices/src/notification-service/Application/EventNotificationResolver.cs
  • microservices/src/notification-service/Application/NotificationDispatcher.cs
  • microservices/src/notification-service/Application/NotificationOptions.cs
  • microservices/src/notification-service/Messaging/NotificationEventEnvelope.cs

Revision Information

  • Status: Draft
  • Last reviewed: 2026-07-21
  • Review cycle: Quarterly