Skip to main content

Welcome

· 5 min read

Software systems are often judged by how they behave in production.
But for the people who build and maintain them, what matters just as much is how easily they can be understood.

This documentation is written with that in mind.

It is not just a reference. It is a guide to how our system is structured, why certain decisions were made, and how you can work effectively within it.

Whether you are onboarding, debugging, or extending functionality, this is where you start.

The Context Behind This System

Not every system needs to be distributed into dozens of services.

In our case, we deliberately chose a modular, non-microservice architecture. This allows us to:

  • Move faster with fewer operational overheads
  • Keep complexity under control
  • Maintain strong boundaries within a single codebase
  • Avoid premature distribution of logic

At the same time, we still adopt modern practices such as containerization and clear separation between backend and frontend concerns.

The result is a system that is simple where it should be, and structured where it needs to be.

High-Level Architecture

The platform is composed of three main layers:

  1. Backend application
  2. Frontend application
  3. Infrastructure layer

Each layer has a clear responsibility, and communication between them is intentional and predictable.

Backend: Structured and Scalable

The backend is built using NestJS.

NestJS provides a strong architectural foundation through:

  • Modular structure
  • Dependency injection
  • Clear separation of concerns
  • Built-in support for scalable patterns

Rather than splitting into microservices, we organize the backend into well-defined modules, each responsible for a specific domain.

This approach gives us many of the benefits of microservices—such as separation and maintainability—without the operational cost.

Typical responsibilities handled by the backend include:

  • Business logic and domain rules
  • API endpoints and request handling
  • Data validation and transformation
  • Integration with external systems

The goal is not just to make the backend functional, but to make it predictable and easy to navigate.

Frontend: Clean and Reactive

The frontend is built using Next.js.

Next.js allows us to combine:

  • Server-side rendering (SSR)
  • Static generation (SSG)
  • Client-side interactivity

This gives us flexibility in how we deliver content and optimize performance.

The frontend is designed with a focus on:

  • Clear user experience
  • Component reusability
  • Maintainable state management
  • Efficient data fetching

It communicates with the backend through well-defined APIs, ensuring a clean separation between presentation and logic.

Containerization and Environment Consistency

Even though we are not using microservices, we still rely on Docker.

Docker allows us to standardize how the application runs across environments:

  • Development
  • Testing
  • Production

By containerizing our applications, we ensure:

  • Consistent runtime environments
  • Simplified onboarding
  • Easier deployment pipelines
  • Reduced "it works on my machine" issues

Each part of the system runs in a controlled and reproducible environment, which is critical for reliability.

Why Not Microservices?

It is easy to assume that microservices are always the better choice. In reality, they introduce significant complexity:

  • Service orchestration
  • Network communication overhead
  • Distributed debugging
  • Deployment coordination

For our current scale and requirements, these trade-offs are not justified.

Instead, we focus on:

  • Strong modular design
  • Clear internal boundaries
  • Maintainable code structure

If the system grows to a point where distribution becomes necessary, the existing modular design will make that transition easier.

How the Pieces Work Together

At runtime, the system operates as a cohesive unit:

  • The frontend (Next.js) handles user interaction and rendering
  • The backend (NestJS) processes requests and enforces business logic
  • Docker ensures everything runs consistently across environments

Requests flow in a straightforward manner:

  1. A user interacts with the frontend
  2. The frontend sends a request to the backend API
  3. The backend processes the request and interacts with the data layer
  4. A response is returned and reflected in the UI

This simplicity is intentional. It reduces cognitive load and makes the system easier to reason about.

What You Will Find in This Documentation

This documentation is structured to help you navigate the system efficiently.

Getting Started

Set up your local environment and run the application using Docker.

Project Structure

Understand how the codebase is organized, including modules, layers, and responsibilities.

Backend Guide

Dive into NestJS modules, services, controllers, and patterns used across the system.

Frontend Guide

Explore how the Next.js application is structured and how it interacts with the backend.

API Reference

Detailed information about available endpoints, request formats, and responses.

Deployment

Instructions on how the system is built, packaged, and deployed.

Principles We Follow

Throughout the system, we try to stay consistent with a few key principles:

  • Clarity over cleverness
    Code should be easy to read and reason about.

  • Structure over shortcuts
    A well-organized system scales better than a quick fix.

  • Consistency over preference
    Patterns should be predictable across the codebase.

  • Simplicity over unnecessary abstraction
    We avoid adding complexity unless it provides clear value.

A Living Document

This documentation is not static.

As the system evolves, new patterns will emerge and existing ones will change. This document should evolve alongside the codebase.

If something is unclear or missing, it is an opportunity to improve—not just the documentation, but the system itself.

Closing

Understanding a system is not about knowing every file or function.

It is about understanding how the pieces fit together and being able to navigate them with confidence.

This documentation is here to help you build that understanding.

From here, you can start exploring the codebase, tracing flows, and contributing effectively.