TypeScript Email SDK implementing the mandatory high-level system design concepts from your PRD.
npm installnpm run typechecknpm run testnpm run example:basicnpm run example:fallbacknpm run example:bulk
See .env.example for provider credentials and SDK settings.
2.1 Provider Abstraction:IEmailProvider,BaseProvider,SmtpProvider,AwsSesProvider,SendGridProvider,ProviderRegistry2.2 Factory:EmailProviderFactory,TemplateFactory2.3 Async Queue + Back-pressure:EmailQueue,QueueWorker,DLQHandler, queue full rejection2.4 Retry + Backoff:RetryPolicywith jitter and retryable/non-retryable code handling2.5 Fallback + Circuit Breaker:FallbackChain, per-providerCircuitBreakerstate machine2.6 Rate Limiting: per-provider token bucket inRateLimiterwith wait/throw behavior2.7 Template Engine:ITemplateEngine,HandlebarsEngine,MustacheEngine, LRUTemplateCache2.8 Observer:EmailEventEmitter,ConsoleLogger,FileLoggerwith JSON logs2.9 DI/IoC: constructor injection inEmailSDK, fluent wiring inSDKBuilder2.10 Analytics:MetricsCollector,HealthChecker,sdk.getStats(),sdk.healthCheck()
┌─────────────────────────────────────────────────────────────────────┐
│ YOUR APPLICATION │
│ const sdk = new SDKBuilder().addProvider(...).build() │
│ await sdk.send({ to, subject, html }) │
└─────────────────────────┬───────────────────────────────────────────┘
│ sdk.send()
▼
┌─────────────────────────────────────────────────────────────────────┐
│ EmailSDK (Core Orchestrator) │
│ • Normalizes payload • Generates messageId + correlationId │
│ • Renders template (if templateId given) │
│ • Enqueues job • Emits email.queued event │
└─────────────────────────┬───────────────────────────────────────────┘
│ enqueue()
▼
┌─────────────────────────────────────────────────────────────────────┐
│ EmailQueue (Priority Queue — max 10,000 jobs) │
│ Sort: nextRetryAt → priority(high/normal/low) → enqueuedAt (FIFO) │
│ Back-pressure: throws QueueFullError if full │
└─────────────────────────┬───────────────────────────────────────────┘
│ dequeue() [polled every 100ms]
▼
┌─────────────────────────────────────────────────────────────────────┐
│ QueueWorker (5 concurrent async loops) │
│ • Continuously polls queue │
│ • On success: calls onProcessed → emits email.sent │
│ • On failure: sends to DLQHandler → emits email.failed │
└─────────────────────────┬───────────────────────────────────────────┘
│ deliver(job)
▼
┌─────────────────────────────────────────────────────────────────────┐
│ DeliveryEngine (Brain) │
│ 1. Gets orderedAvailable providers from FallbackChain │
│ 2. For each provider: │
│ a. RateLimiter.acquire() ← Token Bucket check │
│ b. provider.send(payload) │
│ c. On success: breaker.recordSuccess(), return result │
│ d. On failure: breaker.recordFailure() │
│ RetryPolicy.shouldRetry() → wait delay → retry │
└──────┬──────────────────┬──────────────────┬────────────────────────┘
│ │ │
▼ ▼ ▼
┌────────────┐ ┌──────────────┐ ┌──────────────────┐
│ SES Provider│ │ SMTP Provider│ │ SendGrid Provider │
│ (primary) │ │ (fallback 1) │ │ (fallback 2) │
│ nodemailer │ │ nodemailer │ │ @sendgrid/mail │
│ SESClient │ │ Transporter │ │ sendgrid.send() │
└─────┬──────┘ └──────┬───────┘ └────────┬─────────┘
│ │ │
▼ ▼ ▼
AWS SES Mail Server SendGrid API
(us-east-1) (smtp.example.com) (api.sendgrid.com)
│ │ │
└─────────────────┴────────────────────┘
│
▼
Recipient's Inbox
([email protected])
════════════════════════════════════════════════════════
RESILIENCE LAYER (per-provider)
════════════════════════════════════════════════════════
┌─────────────────────┐ ┌──────────────────────┐
│ CircuitBreaker │ │ RateLimiter │
│ CLOSED→OPEN (5fail) │ │ Token Bucket │
│ OPEN→HALF after 60s │ │ 100 tokens/sec │
│ HALF→CLOSED (1 ok) │ │ Burst: 200 tokens │
└─────────────────────┘ └──────────────────────┘
┌─────────────────────┐ ┌──────────────────────┐
│ RetryPolicy │ │ FallbackChain │
│ Exp Backoff+Jitter │ │ [SES, SMTP, SG] │
│ maxAttempts: 3 │ │ Skip OPEN circuits │
│ Non-retry: 400,401 │ │ Returns next live │
└─────────────────────┘ └──────────────────────┘
════════════════════════════════════════════════════════
OBSERVABILITY LAYER
════════════════════════════════════════════════════════
┌─────────────────────────────────────────────────────┐
│ EmailEventEmitter │
│ Emits: queued | sent | failed | retrying | bounced│
└──────┬──────────────┬──────────────────┬────────────┘
│ │ │
▼ ▼ ▼
┌────────────┐ ┌─────────────┐ ┌─────────────────────┐
│ConsoleLogger│ │ FileLogger │ │ MetricsCollector │
│ JSON→stdout │ │JSON→file.log│ │totalSent, failed, │
└────────────┘ └─────────────┘ │byProvider, queued │
└─────────────────────┘
════════════════════════════════════════════════════════
DLQ (Dead Letter Queue) — permanent failures land here
════════════════════════════════════════════════════════
┌─────────────────────────────────────────────────────┐
│ DLQHandler │
│ failedJobs: QueueJob[] (in-memory array) │
│ .list() → inspect failed jobs manually │
└─────────────────────────────────────────────────────┘
════════════════════════════════════════════════════════
TEMPLATES
════════════════════════════════════════════════════════
┌───────────────────┐ ┌──────────────────────────┐
│ TemplateFactory │ ───▶ │ HandlebarsEngine / │
│ (creates engine) │ │ MustacheEngine │
└───────────────────┘ │ compile() + render() │
└──────────────────────────┘
│
┌──────────────────────────┐
│ TemplateCache (LRU) │
│ max 100 compiled tmpl │
└──────────────────────────┘
System Architecture Diagram :
"



