A fault-tolerant, event-driven backend system that reliably delivers notifications (Email and Webhooks) using asynchronous processing, idempotency, retries, and dead-letter queues.
- Overview
- Technology Stack
- Key Features
- Architecture
- Folder Structure
- Prerequisites
- Quick Start
- Testing & Verification
- CI/CD Pipeline
When important events occur inside a system (e.g., "User Signed Up"), external parties must be notified reliably—even when parts of the system fail. This project demonstrates a Senior-Level backend architecture that decouples event ingestion from processing to ensure responsiveness and reliability.
- Language: TypeScript (Strict Mode)
- Runtime: Node.js
- Database: PostgreSQL (pg)
- Message Broker: RabbitMQ (amqplib)
- Logging: Winston (@shared/logger)
- Testing: Jest, Supertest
- Validation: Zod
- CI/CD: GitHub Actions
- Infrastructure: Docker, Docker Compose
- Event-Driven Architecture: Decouples producers (API) from consumers (Worker) using RabbitMQ.
- Resilience & Reliability:
- Retries: Automatically retries failed jobs with exponential backoff.
- Dead Letter Queue (DLQ): Captures permanently failed messages for inspection.
- Graceful Shutdown: Handles
SIGTERMto safely finish active jobs before exiting.
- Observability:
- Structured Logging: JSON logs via
winstonfor easy parsing. - Correlation IDs: Traces requests from API -> Queue -> Worker.
- Structured Logging: JSON logs via
- Data Integrity:
- Transactional Outbox (Simulated via Flow): Database commit happens before event publication.
- Idempotency: Ensures notifications are sent exactly once per channel.
- Testing:
- Manual Chaos: Toggle
FAILURE_PROBABILITYin.envto test system stability under load.
- Manual Chaos: Toggle
- Security:
- Secret Isolation: All credentials (DB/Queue) are moved to environment variables.
- Environment Templates: Standardized
.env.examplefor secure setup.
- Automation:
- CI/CD: Fully automated test and build verification on push.
- Auto-Migrations: Database schema initializes automatically on container startup.
- Event API: Receives HTTP
POST /events. Validates payload (Zod). Persists to Postgres. Publishes to RabbitMQ. Returns202 Accepted. - RabbitMQ: Buffers messages. Handles routing (Fanout exchange).
- Worker: Consumes messages. Simulates Email/Webhook delivery. Updates status in Postgres.
event-driven-notification-system
├── .github/workflows # CI/CD Workflows
├── services
│ ├── event-api # Express.js API Service
│ │ └── Dockerfile
│ └── worker # Background Consumer Service
│ └── Dockerfile
├── shared
│ └── logger # Shared Winston bundle
├── database # Automatic SQL Migrations
├── docs # ADRs & Architecture detail
├── .env.example # Secrets template
├── docker-compose.yml # System Orchestration
└── package.json # Workspace management
- Docker & Docker Compose
- Node.js (v18+) (Optional, for local dev)
The entire system is containerized. You can spin it up with one command.
-
Environment Setup: Copy the example environment file to the root:
cp .env.example .env
-
Start Services:
docker-compose up --build
-
Access Points:
- API:
http://localhost:3000 - RabbitMQ:
http://localhost:15672(guest/guest) - Postgres:
localhost:5432
- API:
Note: Database tables are created automatically on the first run.
Run the unit test suite (Jest):
npm testSend a test event to see the system in action:
curl -X POST http://localhost:3000/events \
-H "Content-Type: application/json" \
-d '{
"id": "e605d3b6-15df-4206-8c9e-5f9630717208",
"type": "USER_CREATED",
"version": 1,
"payload": { "email": "test@example.com" },
"occurredAt": "2023-01-01T00:00:00Z"
}'Watch the logs! You will see the API accept it, and the Worker process it.
To see the Retry & DLQ logic in action:
- Change
FAILURE_PROBABILITY=1in your.envfile. - Restart the worker.
- The next request will fail 5 times (visible in logs) and then move to the Dead Letter Queue.
The project includes a GitHub Actions workflow that:
- Spins up a real PostgreSQL 15 instance.
- Runs full Jest test suites.
- Verifies Docker image compilation for both services.
Checks run automatically on all pushes to main and develop.