How I Used PostgreSQL Events and MongoDB Event Logs in Two Different Real-World Systems

How I Used PostgreSQL Events and MongoDB Event Logs in Two Different Real-World Systems

calendar_today agoschedule4 min read
— Originally published at dev.to

I built two systems that both needed event tracking.
I used PostgreSQL in one and MongoDB in the other.
Here's exactly why — and what I learned from doing both.
Although the two systems were completely different in purpose, they shared one architectural requirement: each needed a reliable way to record everything that happened — not just the final state, but the story of how that state came to be.
That sounds simple, but the nature of the events in each system was so different that using the same database for both would have caused real problems. This article explains how I approached event tracking in each system, why the solutions diverged, and what the experience taught me about choosing the right persistence model.

PostgreSQL Events and Projections in the Delivery Backend

The delivery backend was built around structured workflows: vendors create orders, couriers pick them up, jobs move through well-defined states, and batches group multiple deliveries. Every action in this system has a clear relational meaning. An event like ORDER_CREATED or JOB_STARTED isn't just a log entry — it's part of the domain model.
Because of that, PostgreSQL was the natural home for events. Each event needed to be tied to a specific entity, validated against constraints, indexed for fast lookup, and projected into timeline tables that the dashboard could query efficiently.
The event log table looked like this:

model EventLog {
  id          Int             @id @default(autoincrement())
  entityType  EventEntityType
  entityId    Int
  eventType   String
  payload     Json
  createdAt   DateTime        @default(now())

  @@index([entityType, entityId, createdAt])
}

Every event is immutable. Every event belongs to a relational entity. Every event carries structured metadata.
Once an event is written, it is immediately transformed into a timeline entry:

switch (event.entityType) {
  case EventEntityType.ORDER:
    await this.orderTimelineProjection.project(event);
    break;

  case EventEntityType.JOB:
    await this.jobTimelineProjection.project(event);
    break;
}

This projection layer is what makes the system feel fast. Instead of running complex joins to reconstruct history, the dashboard reads from dedicated timeline tables optimized for sequential queries. It also makes debugging easier: if something goes wrong, I can replay the event stream and see exactly how the system reached its current state.
PostgreSQL excels in this environment because the events are structured, relational, and part of the domain's consistency model. They aren't just analytics — they're part of the truth.

MongoDB Event Logs and Aggregation in Waitless

Waitless is a completely different kind of system. It's a real-time queue management platform where customers join via QR code, track their position live, and leave without creating an account. The system generates a lot of events — but these events are not relational. They're behavioral.
A CUSTOMER_JOINED event might contain the customer's position.
A NO_SHOW event might contain a timestamp.
A CUSTOMER_LEFT event might contain a reason.
A CALLED_NEXT event might contain which token was called.

The shape of the metadata changes depending on the event type. Forcing this into a rigid SQL schema would either require dozens of nullable columns or a JSON column that PostgreSQL would struggle to index efficiently at scale.
MongoDB is built for exactly this. Each event is a document with flexible metadata:

@Schema({ timestamps: true })
export class QueueEvent {
  @Prop({ required: true })
  queueId!: string;

  @Prop({ required: true, enum: QueueEventType })
  eventType!: QueueEventType;

  @Prop()
  token?: string;

  @Prop({ type: Object })
  metadata?: Record<string, any>;
}

This flexibility becomes powerful when combined with MongoDB's aggregation pipeline. Waitless computes analytics like total joined, total arrived, total no-shows, completion rate, and drop-off rate — all derived from event counts, not relational queries.
A note on Redis: Waitless already had Redis in the stack backing BullMQ. But Redis is a key-value store optimized for job queues and caching — not for querying across documents or running aggregations. MongoDB was the right fit for this specific need.
The separation between MongoDB and PostgreSQL is also crucial for performance. If analytics were computed in PostgreSQL, every dashboard refresh would compete with real-time queue operations for the same connection pool.

If I had used PostgreSQL for Waitless events, every analytics query would hit the same connection pool as the queue state — meaning a slow dashboard query could delay a customer joining a queue.
MongoDB avoids this entirely. It handles flexible event shapes, high write throughput, and analytics workloads without interfering with the transactional state stored in PostgreSQL.

Why PostgreSQL for One and MongoDB for the Other?

The answer becomes clear once you see the nature of the data.
In the delivery backend, events are part of the domain model. They describe state transitions, belong to relational entities, and require strong consistency. PostgreSQL is the right tool.
In Waitless, events are part of analytics. They are flexible, metadata-heavy, and high-volume. They have no relational meaning — they're behavioral observations about what happened in a queue session. MongoDB is the right tool.
Using PostgreSQL for both would have created unnecessary coupling and performance bottlenecks. Using MongoDB for both would have sacrificed relational guarantees where they mattered most.
This is the essence of polyglot persistence: choose the database that matches the shape and purpose of the data, not the one you're most comfortable with.

The Question That Actually Matters

Building two event-driven systems taught me that "SQL vs NoSQL" is the wrong question entirely.
The real question is: what role do your events play in the system?
If events belong to the domain model — use a relational database.
If events belong to analytics — use a document database.
PostgreSQL gave me structured timelines and strong consistency. MongoDB gave me flexible event logs and fast aggregation. Both systems use events, but the events serve completely different masters.
Understanding that difference before writing a single line of code is what separates a working system from a well-designed one.

🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

The Sovereign Vault — A Comprehensive Guide to Protocol-Driven AI

Ken W. Algerverified - Jun 4

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

Tooling Tips: Visualizing Your Data in MongoDB and Kafka

rvneto - May 5

The Hidden Program Behind Every SQL Statement

lovestaco - Apr 11
chevron_left
128 Points3 Badges
1Posts
0Comments
Hello everyone, I'm a Backend Engineer. My main stack is TS , NestJS and PostgreSQL , currently expa... Show more

Related Jobs

View all jobs →

Commenters (This Week)

4 comments
2 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!