Notification System Design
Notification system design tests Strategy + Factory + Observer + Outbox composition.
Introduction
Notification system design tests Strategy + Factory + Observer + Outbox composition. Naive if/else channel dispatch breaks within a quarter. This lesson walks the scalable canonical design.
The story
SaaS notifications started as one function with 4 if-branches. 18 months later: 47 branches across 8 functions, 6 files to send one notification. Rebuild: Strategy per channel + Factory selection + Outbox reliability + Observer triggering. New channel = one file.
The business problem
Notification sprawl blocks every product launch:
- 47 if-branches: email/SMS/push/webhook/slack duplicated.
- 6 files per send: no single seam to extend.
- Unreliable delivery: fire-and-forget HTTP in request path.
- Compliance: opt-out, rate limit, audit per channel.
The problem teams faced
Notification system forces:
- Multiple channels with different APIs and retry policies.
- Trigger from many domain events — OrderPlaced, PasswordReset.
- Reliable async delivery — not in critical request path.
- Easy addition of new channels without editing dispatch logic.
Understanding the topic
Intent: Extensible multi-channel notification architecture.
- Strategy — one impl per channel (Email, Sms, Push).
- Factory — select strategies by user preferences + event type.
- Observer — domain events trigger notification pipeline.
- Outbox — reliable enqueue from transactional context.
Internal architecture
Notification service structure:
interface NotificationChannel {send(recipient: Recipient, template: Template, data: Record<string, unknown>): Promise<void>}class NotificationDispatcher {constructor(private factory: ChannelFactory) {}async dispatch(event: DomainEvent, user: User) {const channels = this.factory.resolveChannels(event.type, user.preferences)await Promise.all(channels.map(ch => ch.send(user, event.template, event.data)))}}// Order service: Outbox.write(OrderPlaced) in same transaction as order save
Visual explanation
Three diagrams cover channel Strategy, event pipeline, before/after if-else:
Informative example
Before / after dispatch:
// ❌ 47 if-branches across 8 functionsfunction notify(user, type, data) {if (type === "email") { /* sendgrid */ }else if (type === "sms") { /* twilio */ }// ... 45 more}// ✅ Strategy + Factoryclass ChannelFactory {resolve(eventType: string, prefs: Prefs): NotificationChannel[] {return prefs.enabledChannels.map(name => this.registry.get(name)!).filter(ch => ch.supports(eventType))}}
Execution workflow
Audit if-branches
Count channel conditionals.
Real-world use
Twilio SendGrid architecture; LinkedIn notification tier; Uber multi-channel dispatch; AWS SNS + SES patterns.
Production case study
47 if-branch rebuild:
- Before: 4 branches → 47 in 18 months; 6 files per send.
- Rebuild: Strategy + Factory + Outbox + Observer.
- Outcome: new channel in one file, one PR.
Trade-offs
- Pro: open-closed for channels; reliable async; testable per Strategy.
- Con: more files; Factory registry maintenance.
Decision framework
- 2+ channels → Strategy per channel minimum.
- Cross-service triggers → Outbox + notification consumer.
- Single email only → don't build full platform yet.
Best practices
- Contract test each NotificationChannel Strategy.
- Template engine shared; channel Strategy handles transport only.
- Dead letter queue for failed sends after retries.
Anti-patterns to avoid
- Sync send in HTTP request path — latency + reliability risk.
- Fat NotificationService with all channel logic inline.
- Observer across services via direct HTTP callbacks.
Common mistakes
- Factory returns wrong channels — test preference matrix.
- Duplicate notifications on Outbox relay retry — idempotent send keys.
Debugging tips
- Trace: event → outbox → relay → dispatcher → which Strategy failed.
Optimization strategies
- Batch email sends; rate limit SMS per user.
Common misconceptions
- Observer ≠ entire system — Observer triggers; Strategy delivers.
- Notification ≠ email only — multi-channel from day one if roadmap says so.
Advanced interview questions
Interview Prep
Practice concise answers, then expand each card for the explanation.
1AdvancedQuestionDesign notification system?+
Answer
Follow-up
2IntermediateQuestionWhy Outbox for notifications?+
Answer
Follow-up
3IntermediateQuestionStrategy vs if-else?+
Answer
Follow-up
Summary
You can design extensible multi-channel notifications. Tell the 47-branch rebuild — you own Notification System Design.