webhook-processor

Webhook Processor

CI Status

🇧🇷 Leia em Português

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.


🏗 Logical Flow

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)]

🚀 Key Features & Decisions

1. Robust Idempotency

Prevents “double spending” or duplicate side effects when providers send the same event multiple times.

2. Entity Ordering (Concurrency Control)

Ensures that OrderCreated is always processed before OrderPaid.

3. Complete Auditability

All decisions—whether accepted, ignored, processed, or rejected—are permanently logged.


đź›  Tech Stack


⚡ Quick Start

1. Setup Environment

cp .env.example .env

2. Start Infrastructure

docker-compose up -d

3. Install & Migrate

npm install
npm run db:migrate

4. Run Locally

npm run dev
# Swagger Docs at http://localhost:3000/api-docs

⚠️ Known Limitations (Architectural Trade-offs)

This 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.

👨‍💻 Author

Gérson Resplandes
Backend Engineer focused on Distributed Systems and Reliability.

LinkedIn Email