Hirzam Chat

Java Vert.x PostgreSQL WebSocket SockJS

What It Is

Hirzam is a real-time chat application where messages appear instantly across all connected browsers without polling. The backend is built on Vert.x 5 using the Verticle model, backed by PostgreSQL for persistence and SockJS for live message broadcasting. It exposes a full CRUD REST API alongside the real-time channel.

Why I Built It

I wanted to work with an event-driven, non-blocking architecture that wasn't Spring Boot. Vert.x's Verticle model — where components are isolated units communicating through an internal event bus — is a fundamentally different approach to building Java backends. This project was my way of learning that model by building something concrete with it.

Architecture

The application is split into three Verticles, each with a single responsibility:

MainVerticle is the entry point. It deploys the other two Verticles in sequence — database first (so the schema exists before any HTTP requests arrive), then HTTP.

DatabaseVerticle owns all SQL interaction. It initializes a JDBC connection pool (max 10 connections) to PostgreSQL, creates the messages table on startup if it doesn't exist, and listens on the Vert.x Event Bus for data requests. It exposes operations for fetching the last 20 messages, inserting new ones, updating content, and soft-deleting by ID.

HttpVerticle runs the HTTP server on port 8080. It serves static files from webroot/, mounts the REST API routes, and manages the SockJS bridge at /eventbus. Critically, it never touches the database directly — every data operation goes through an eventBus.request() call to DatabaseVerticle, which replies asynchronously.

This separation means you could swap the database layer entirely without touching the HTTP code. The service proxy layer (MessageService interface with MessageServiceImpl) adds another level of decoupling that also makes the Verticles independently testable.

The Real-Time Flow

When a user sends a message, the flow is: browser POSTs to /api/messagesHttpVerticle sends an event bus message → DatabaseVerticle inserts the row and replies → HttpVerticle responds with 201 and simultaneously publishes the new message on the SockJS bridge. Every connected browser receives it instantly through their open SockJS connection — no polling, no refresh.

The same broadcast pattern applies to edits (ws.updatedMessage) and deletions (ws.deletedMessage), so all clients stay in sync.

Key Decisions

Event Bus over direct calls was the defining architectural choice. In a typical Java web app, the HTTP handler would call a service which calls a repository. Here, Verticles are isolated — they don't share objects or call each other's methods. Communication is message-passing over the event bus, which keeps each Verticle single-threaded and eliminates shared-state concurrency issues.

Soft deletes instead of hard deletes for messages. The deleted flag keeps the row in the database, which is better for audit trails and prevents foreign key issues if message threading is added later.

What I Learned

The Vert.x mental model — don't block the event loop, communicate through messages, think in callbacks and futures — is a shift from traditional Java. It taught me why event-driven architectures scale well for I/O-bound workloads: one thread can handle thousands of connections when you're never blocking on database calls or network I/O.

Setting up the SockJS bridge also clarified how WebSocket-style communication works under the hood — SockJS provides fallbacks for environments where raw WebSockets aren't available, which matters more than you'd expect in practice.

What's Next

The obvious gaps are authentication (currently anyone can edit or delete any message) and externalizing the database credentials from hardcoded values to environment variables or a config file. Beyond that, adding user presence indicators, message threading, and file attachments would make it a more complete messaging platform.