Skip to main content

Notification Operational Architecture

Summary

A single .NET 8 process hosting one HTTP surface and one background consumer, with two runtime dependencies: PostgreSQL and RabbitMQ. The two behave very differently when unavailable.

Audience

DevOps engineers, engineers, support, architects and QA.

Service startup

Operational dependencies

DependencyRequired forBehavior when unavailable
PostgreSQLEverything — all reads and writesStartup fails at the migration step unless migrations are skipped
RabbitMQEvent consumption onlyStartup succeeds; the consumer retries indefinitely in the background
SMTP hostEmail delivery, only when the SMTP provider is selectedDelivery attempts fail and retry; the service stays up

This asymmetry is the single most important operational fact about the service. A database outage prevents the process from starting; a broker outage leaves a fully healthy-looking process that consumes nothing.

Service lifecycle

Start. Migration and seeding run synchronously before the host serves traffic, inside a temporary scope. The hosted consumer starts after the host is running and connects on its own schedule, so HTTP is available before consumption begins.

Steady state. One consumer holds one connection and one channel, with automatic recovery enabled and a fixed network recovery interval. Each delivery is processed in its own dependency-injection scope. HTTP requests are handled independently with their own scopes.

Broker reconnect. Connection failures are caught by an outer loop that logs a warning naming the host and port, waits a fixed interval and retries — indefinitely, with no attempt cap and no escalation.

Stop. The base stop completes first, then the channel and connection are disposed. Because the consumer passes its stopping token into dispatch, an in-flight event is cancelled mid-processing rather than drained, which given the many-small-saves persistence model can leave a partially processed event with no idempotency record.

Scaling posture

Nothing in the reviewed source prevents multiple instances. All instances would declare the same durable queue and compete for deliveries, which is a valid competing-consumer arrangement.

Two consequences follow from source: every instance applies migrations at startup, so a rollout produces concurrent migration attempts; and prefetch applies per consumer, so total in-flight work scales with instance count while ordering guarantees remain absent.

Deployment inputs

The service reads configuration from its content root, so it must be started from a directory containing its settings file — which is why the smoke script starts it from its build output directory.

A single environment variable, the migration skip flag, alters startup behavior. It also suppresses seeding.

Classification

Transitional.

Requires confirmation

Target instance count, orchestration probes, and whether migrations should move out of startup require confirmation.

See Also

Keywords

  • Notification database
  • Operational Architecture
  • Draft database documentation

Source References

  • microservices/src/notification-service/Program.cs
  • microservices/src/notification-service/Messaging/NotificationEventConsumer.cs
  • microservices/src/notification-service/Application/NotificationOptions.cs
  • microservices/src/notification-service/Seed/DefaultTemplateSeeder.cs
  • microservices/src/notification-service/Properties/launchSettings.json

Revision Information

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