From Monolith to Modular (A basic simple example)

Published: (December 9, 2025 at 04:29 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

How I’m learning System Design by breaking my own code.

Phase 1: The Simple Single Server (2‑Tier)

Repository: arsalanbardsiri/simple-single-server-app

Every great system starts as a monolith. My first iteration was a classic “2‑Tier” application. In this setup the architecture is deceptively simple:

  • Client Tier: The user interface (browser).
  • Server Tier: A single machine handling both the business logic (Web Server) and the data persistence (Database).

The Architecture

Architecture diagram

Why start here?

It is the fastest way to ship. You have zero network latency between your application and your database because they live on the same hardware. Deployment is a single command. For a student project or a proof‑of‑concept, this is perfect.

The Wall

However, this simplicity is a trap.

  • Vertical Scaling Limits: To handle more users you must upgrade the entire server (more RAM, better CPU). You cannot upgrade the database independently.
  • Single Point of Failure (SPOF): If the web server crashes due to a buggy script, the database goes down with it.
  • Security Risk: In this 2‑tier setup the database often shares the same environment as the public‑facing web server, increasing the attack surface.

Phase 2: The Evolution to 3‑Tier Architecture

Repository: arsalanbardsiri/3tier-simple-app

To solve the fragility of the single server, I refactored the application into a 3‑Tier Architecture. This is the industry standard for a reason. It physically and logically separates the system into three distinct layers.

The Architecture

3‑Tier diagram

  • Presentation Tier: The frontend (client) that the user sees.
  • Application Tier: The backend API (business logic) that processes requests.
  • Data Tier: The dedicated database server that stores information.

The “Aha!” Moment

Moving to this architecture introduced complexity—I now have to manage connection strings, network latency, and multiple deployments. So, why do it?

  1. Independent Scalability
    If the application performs heavy calculations (CPU‑intensive), I can add more Application Servers without touching the database. If it stores massive amounts of logs (storage‑intensive), I can upgrade the Database Server independently.

  2. Improved Security
    In the single‑server app the database lived on the public‑facing server. In the 3‑tier app the Database Tier is hidden behind the Application Tier, residing in a private network layer accessible only by the Application Server.

  3. Fault Tolerance
    By decoupling the components, the system can tolerate restarts. Restarting the web server to deploy a patch does not affect the database.

What’s Next?

I have successfully separated the concerns, but I still have a single point of failure: only one Application Server and one Database Server. The next step in the ByteByteGo roadmap is to implement a Load Balancer (likely NGINX) to distribute traffic across multiple application servers, ensuring that if one goes down, users never notice.

Follow the code evolution

Back to Blog

Related posts

Read more »