|
← Back to Home

Case Study: Discol

A technical deep dive into the architecture and engineering decisions behind a full-stack real-time chat platform with voice rooms, role-based permissions, and production-grade deployment.

00PROBLEM

Problem & Goals

Modern chat platforms feel simple on the surface, but they rely on a dense mix of systems behind the scenes: identity, real-time communication, permissions, presence, media, and deployment reliability. Most portfolio projects stop at the UI layer. The challenge was to go deeper.

The goal was to design and ship a complete real-time product — a system where users can register, connect with friends, join communities, chat instantly, manage roles and permissions, enter voice rooms, and interact with a guided demo experience. Just as importantly, the project had to reflect production-minded engineering practices: automated testing, containerized environments, and continuous delivery.

01SOLUTION

What Was Built

Discol is a full-stack real-time chat application built around servers, channels, friendships, and role-based collaboration. Users can sign up with email and password or Google OAuth, create servers, invite other users, organize conversations into text and voice channels, and manage server membership through roles and permissions.

Real-time Messaging

Text messages delivered instantly via Socket.IO. Presence updates are pushed only to relevant users rather than broadcast globally.

Voice Rooms

Channel-based audio and video sessions powered by LiveKit, accessible directly from within the application.

Role-based Permissions

Explicit server permissions for channel creation, invite management, role management, and server deletion modeled in the database.

Media Uploads

Profile avatar uploads through Cloudinary with cloud storage and serving.

Demo Mode

Seeded content and a guided product tour so recruiters and reviewers can explore the product without populating it manually.

Localization

English and Spanish language support built into the frontend interface.

02DESIGN

UI/UX Design Decisions

The interface was designed around familiar messaging patterns to minimize the learning curve for users. The layout follows a three-column structure consisting of server navigation, channel list, and active chat view.

A dark theme was chosen to improve readability during long messaging sessions and match the design conventions of modern collaboration tools.

Discord Clone UI Mockup
03ARCHITECTURE

System Architecture

The frontend is a Next.js 16 application using the App Router, React 19, and Tailwind CSS v4. It handles page routing, responsive UI, long-lived client state, and real-time event subscriptions. Shared contexts keep the interface synchronized as events arrive.

The backend is a NestJS 11 modular monolith. Focused modules cover authentication, users, friendships, servers, roles, channels, messages, voice access, and real-time gateway events. All REST endpoints are exposed under /api, while Socket.IO runs alongside for low-latency messaging.

For data persistence, the app uses PostgreSQL with Prisma ORM. The schema models users, OAuth accounts, friendships, servers, invites, members, roles, channels, and messages. Role-based access control is enforced through explicit server permissions, keeping authorization rules visible and maintainable.

The real-time layer uses two communication patterns: channel rooms for chat fanout (only users in the active channel receive live messages) and user-specific rooms for private notifications like friend requests, server invites, and presence changes.

System Architecture Diagram
04AUTHENTICATION

Authentication Flow

05REAL-TIME MESSAGING

Message Flow

Message Flow Diagram
06TECH STACK

Technology Decisions

Frontend

Next.js 16, React 19, Tailwind CSS v4

App Router with server components, responsive layouts, long-lived client state, and real-time event subscriptions.

Backend

NestJS 11, Prisma ORM, PostgreSQL

Modular monolith with focused domain modules. Prisma models the full collaboration schema with explicit RBAC permissions.

Real-time

Socket.IO

Persistent bidirectional connections for instant message delivery and targeted user notifications.

Voice & Video

LiveKit

Channel-based audio and video sessions accessible directly from within the application.

Authentication

JWT, Passport, Google OAuth 2.0

Supports email/password registration and Google OAuth. JWTs are validated across both REST and WebSocket layers.

Media Storage

Cloudinary

Profile avatar uploads with cloud storage, allowing users to personalize their accounts.

Testing

Jest, Supertest, Playwright

Layered test strategy: frontend unit tests, backend unit and integration tests, and browser-level end-to-end specs.

DevOps

Docker, Docker Compose, GitHub Actions, GHCR, VPS/SSH

Multi-stage container builds, automated CI pipelines, and production deployment over SSH to a Linux VPS behind Nginx.

07CI/CD PIPELINE

Continuous Integration & Deployment

08CHALLENGES

Challenges & Lessons Learned

01

Making real-time features feel selective instead of noisy

Messaging, status updates, channel changes, invites, and friendship actions all have different audiences. The solution was to separate channel-level events from user-level events — messages are emitted only to the relevant channel room, while friend and invite notifications are emitted directly to the affected users. This kept the real-time layer clean and closer to how a production chat system should behave.

02

Keeping authorization understandable as features expanded

As soon as server management was added, authorization became more complex than simple ownership checks. Channel creation, role management, inviting users, renaming a server, and removing members all needed explicit rules. Instead of scattering those checks across controllers and services, a dedicated permission system was modeled in Prisma and enforced through NestJS guards — giving the project a clear RBAC story that is easy to explain and extend.

03

Verifying a real-time product across multiple layers

A real-time app can appear to work while hiding fragile behavior across API flows, sockets, and UI state. To reduce that risk, a layered test strategy was built: backend unit tests for service and module logic, backend integration tests against a real PostgreSQL database, frontend unit tests for components and client utilities, and Playwright end-to-end tests that boot the full stack in Docker. That approach gave the project much stronger credibility than a visual demo alone.

09RESULTS

Outcomes

Discol became a strong end-to-end case study in building and shipping a modern real-time product. It demonstrates full-stack ownership across product design, domain modeling, authentication, authorization, WebSocket workflows, voice integration, media uploads, internationalization, testing, and deployment.

53Frontend unit tests
34Backend unit tests
14Backend integration tests
9Playwright e2e specs

All tests run automatically in GitHub Actions pipelines that gate pull requests before deployment — making the project not just a feature demo, but a credible example of engineering quality and delivery from start to finish.