Reliable webhook processing service that implements Idempotency, Strict Ordering by Entity, and Audit Logging. Built to demonstrate correctness in handling distributed events.
Portfolio Note: This project prioritizes architectural correctness, clean code, and transparency in trade-offs. It serves as a reference implementation for securely handling asynchronous event processing patterns.
flowchart TD
In([Webhook Request]) --> Validate{Valid Payload?}
Validate -- No --> R400[400 Bad Request]
Validate -- Yes --> Idem{Idempotency Check}
Idem -- Duplicate & Processed --> R409[409 Conflict]
Idem -- Duplicate & Failed --> Retry[Retry: Reset & Accept]
Idem -- New Event --> Accept[202 Accepted]
Retry --> Queue
Accept --> Queue
Queue --> Lock{Acquire Mutex}
Lock --> Process[Sequential Processing]
Process -- Success --> DB_OK[(DB: PROCESSED + Audit)]
Process -- Error --> DB_FAIL[(DB: FAILED + Audit)]
Prevents "double spending" or duplicate side effects when providers send the same event multiple times.
- Mechanism: Database uniqueness constraint on
event_id. - Enhancement: Distinguishes between "Already Processed" (Conflict) and "Previous Failure" (Retry Allowed). This prevents the common "zombie state" problem where failed events block new attempts.
Ensures that OrderCreated is always processed before OrderPaid.
- Mechanism: In-memory mutex/locking per
customer_id. - Goal: Allows parallel processing between different customers while maintaining a strict sequence for each customer.
- Safety: Implements a "Queue Safe Promise" pattern to ensure that a single failure does not cause a permanent deadlock in the client's queue.
All decisions—whether accepted, ignored, processed, or rejected—are permanently logged.
- Why? Essential for debugging production issues where providers claim "we sent it" but the system state disagrees.
- Runtime: Node.js 20+ (TypeScript Strict Mode)
- Framework: Express
- Database: MySQL 8.0 (via Docker)
- ORM: Prisma (Schema and Migrations)
- Testing: Jest + Supertest (Unit and Integration Mocks)
- Validation: Zod
- Documentation: Swagger/OpenAPI
cp .env.example .envdocker-compose up -dnpm install
npm run db:migratenpm run dev
# Swagger Docs at http://localhost:3000/api-docsThis architecture makes explicit trade-offs for simplicity and consistency in a single-node environment.
| Constraint | Implication | Production Solution (Scale) |
|---|---|---|
| In-Memory Locks | Scaling to multiple instances breaks ordering guarantees. | Redis Lock (Redlock) or Kafka Partitioning (keyed messages). |
| No Dead Letter Queue | Failed events remain FAILED in the database. |
Separate DLQ table + Retry mechanism. |
| Synchronous DB Writes | Higher latency on accepting requests. | Decouple ingestion (SQS/RabbitMQ) from storage. |
Gérson Resplandes
Backend Engineer focused on Distributed Systems and Reliability.