-
This project is an exploration of a scalable chat architecture. It's not a full-featured application but rather a proof-of-concept to demonstrate how different technologies can work together to build a robust, real-time messaging system.
-
No authentication, no proper validation and constraints, just wanted to play with few things.
WebSockets are inherently stateful - once a client connects to a server, they're bound to that instance. This creates challenges:
- Server Affinity: Clients stick to one server, limiting load distribution
- Message Routing: In multi-server setups, finding which server holds a recipient's connection is complex
- Connection Limits: Each server can only handle a finite number of concurrent connections
This architecture solves these issues by decoupling message handling from connection management using Kafka and Redis.
The system is designed to be horizontally scalable. The core idea is to decouple the components so that they can be scaled independently.
- Message Broker (
Kafka): When a user sends a message, it's published to a Kafka topic. This decouples message production from consumption and enables reliable message persistence to the database. - Pub/Sub (
Redis): A service subscribes to the Kafka topic, processes the message, and then publishes it to a Redis Pub/Sub channel. This allows multiple backend server instances to be notified of the new message. - WebSockets (
Socket.IO): Each backend server instance maintains a set of WebSocket connections. When a message is received from the Redis channel, the server finds the correct client and pushes the message to them in real-time using Socket.IO. - Database (
PostgreSQL): Messages are persisted in a PostgreSQL database for history and retrieval.
- Frontend: Next.js, TypeScript, Socket.IO Client
- Backend: Node.js, TypeScript, Socket.IO
- Message Broker: Kafka
- Pub/Sub: Redis
- Database: PostgreSQL with Prisma ORM
- Monorepo: Turborepo & pnpm
- Containerization: Docker & Docker Compose
To get the entire stack running on your local machine, simply use Docker Compose:
docker-compose up