Pathubs Logo Mark
PATHUBSFREE CAREER ROADMAPS
HomeExploreDiscoverCompare ⚖️My Progress 📊Support
Student Support & Feedback

Have Questions or Need Help?

Have questions, feedback, or suggestions for new roadmaps and interactive tools? Reach out to our team — we review every message to make practical learning better for everyone.

supportpathubs@gmail.com Official Telegram Support (@PathubsSupport)
Pathubs

100% Free, Zero-Paywall Tech Career Roadmaps, In-Depth Practical Content, and Live Interactive Virtual Labs for Learners Worldwide.

Popular Careers

  • Frontend Development
  • Backend Development
  • AI & LLM Engineering
  • Full Stack Web Dev
  • Data Analytics

Platform Tools

  • Career Discovery Quiz
  • Compare Careers

Contact & Info

  • About Us
  • supportpathubs@gmail.com
  • Support Pathubs

© 2026 Pathubs. All Rights Reserved. Structured learning, practical content, and hands-on practice for learners worldwide.

AboutPrivacy PolicyTerms & ConditionsSitemapRobots
  1. Roadmaps
  2. Backend Architecture
  3. Design Patterns & Structure
  4. Project Structure
Design Patterns & StructureArchitecture & OrganizationExpress 5.x & FastAPI Hands-On Refactoring

Backend Project Structure

Master how to organize backend code as applications evolve from single-file prototypes to modular enterprise systems. Explore Layer-based vs. Feature-based architectures, modern Express 5.x and FastAPI multi-file patterns, and refactor a messy monolithic application hands-on.

Core Architectural Rule: There is NO single "perfect" universal folder structure. Express.js imposes zero folder conventions, and FastAPI provides flexible modular routing via APIRouter. Project structure should match responsibility, team size, and domain complexity—not blind habit or over-engineered dogma.

The Natural Evolution of Backend Architecture
STAGE 01
Single-File MVP
app.js / main.py (Fast prototypes < 200 lines)
STAGE 02
Layered Structure
routes/, controllers/, services/, repositories/
STAGE 03
Feature-Based / Modules
modules/orders/, modules/users/, screaming architecture
Framework Independence

Express and FastAPI are unopinionated. You control file structure; the framework only handles routing and HTTP cycles.

app.js vs server.js

Separating app assembly from network socket listening unlocks supertest integration testing without port binding.

Beware Over-Engineering

More folders does not mean better code. Avoid building 12 abstraction layers for simple CRUD microservices.

Curriculum Directory & Milestones
01 Why Project Structure Matters02 Common Layered Backend Structure03 Layer-Based vs. Feature-Based Organization04 Express 5.x Project Structure (app vs server)05 FastAPI Official Multi-File Structure06 Interactive Refactoring Studio07 Production Debugging (7 Structural Flaws)08Architectural Challenge & 5 Golden Rules09 Mastery Assessment Quiz
SECTION 01

Why Project Structure Matters

Understand how technical debt compounds when a backend stays inside a single file, and what thoughtful organization provides.

The Breakdown of the Monolith File

Every backend begins with a humble app.js or main.py. At 50 lines, it feels blazingly productive. But as features accumulate:

  • Lost Discoverability: Finding the order discount formula requires scrolling through 1,800 lines of unrelated code.
  • Merge Conflicts: Three developers editing the same file concurrently cause constant Git merge collisions.
  • Impossible Isolation: You cannot unit test order calculations without also initializing database connections, Express middleware, and socket listeners.
  • Hidden Couplings: Database queries, HTTP status codes, and business calculations blur into unmaintainable spaghetti.

The 5 Virtues of Good Structure

A well-structured backend achieves five critical engineering objectives:

  • Separation of Concerns: Routing, business rules, and database persistence live in distinct, dedicated files.
  • Predictable Discoverability: Any new engineer knows immediately where database queries live (repositories/) or where routes are mounted (routes/).
  • Painless Testability: Services and repositories can be tested in isolation with fast in-memory stubs.
  • Parallel Team Velocity: Squads can work on different domain modules simultaneously without touching common files.
  • Refactoring Safety: Upgrading from MongoDB to PostgreSQL touches repositories without altering HTTP controllers.
SECTION 02

Common Layered Backend Structure

See how the concepts you have learned—Controllers, Services, Repositories, and Middleware—fit together inside a cohesive project hierarchy.

src/
├── config/ # Env vars, DB pool, third-party keys
│ ├── db.js
│ └── env.js
├── routes/ # Endpoint URL routing & middleware mapping
│ ├── orderRoutes.js
│ └── userRoutes.js
├── controllers/ # HTTP request parsing & response status
│ ├── orderController.js
│ └── userController.js
├── services/ # Business rules, calculations, orchestration
│ ├── orderService.js
│ └── userService.js
├── repositories/ # Data persistence & parameterized SQL
│ ├── orderRepository.js
│ └── userRepository.js
├── middleware/ # Auth, error handling, rate limiting
│ ├── authMiddleware.js
│ └── errorHandler.js
├── app.js # App configuration & route mounting
└── server.js # HTTP listener, port binding & shutdown

How the Pieces Fit Together

In this architecture, every directory has a single technical responsibility:

  • routes/: Defines HTTP verbs (GET, POST) and URL paths. Attaches route-specific middleware.
  • controllers/: The HTTP traffic cop. Extracts req.body, calls the service, sends res.status(200).json(...).
  • services/: Pure business logic. Calculates taxes, evaluates discounts, orchestrates external email dispatchers.
  • repositories/: Talks to the database. Runs parameterized queries and maps rows to domain entities.
  • config/: Centralizes environment parsing (process.env.DATABASE_URL) with validation.
SECTION 03

Layer-Based vs. Feature-Based (Vertical Slicing)

Compare the two dominant architectural layouts for growing backends. Learn when technical layering works best and when feature slicing excels.

1. Layer-Based (Horizontal Layering)

Code is grouped by technical responsibility (all controllers in one folder, all services in another).

src/
├── controllers/ (userController, orderController)
├── services/ (userService, orderService)
├── repositories/ (userRepo, orderRepo)
└── routes/ (userRoutes, orderRoutes)
  • Pros: Intuitive for beginners; enforces strict architectural tier boundaries.
  • Cons: High friction for feature development: modifying "Orders" requires jumping between 4 distant root folders.
  • Best for: Small-to-medium applications (1-10 domain entities) and small teams.

2. Feature-Based (Vertical Slicing / Screaming)

Code is grouped by business domain context (all files related to orders live together).

src/modules/
├── users/ (routes, controller, service, repo)
├── orders/ (routes, controller, service, repo)
├── products/ (routes, controller, service, repo)
└── shared/ (database, middleware, errors)
  • Pros: High cohesion; autonomous squads can own whole modules without merge conflicts; easy to delete or spin off into microservices.
  • Cons: Cross-domain communication requires clear interfaces; shared utilities need strict curation.
  • Best for: Large applications (20+ entities), multiple squads, domain-driven architectures.
CriteriaLayer-Based OrganizationFeature-Based Organization
Primary Organizing AxisTechnical role (Controller, Service, Repo)Business Domain (Users, Orders, Billing)
Navigation FrictionHigh (Jump between 4-5 folders per feature)Low (All files for one feature are co-located)
Team ScalingMerge conflicts common when squads touch common root foldersSquads own isolated module directories with zero friction
Microservice ExtractionDifficult (Code scattered across multiple layer directories)Trivial (Cut-and-paste the entire module folder)
Optimal Project SizeEarly-stage MVPs, small codebases, monolithic utilitiesEnterprise systems, medium-to-large multi-team codebases
SECTION 04

Express 5.x Project Structure (app vs server)

Learn why decoupling app.js from server.js is the single most important habit in modern Node.js backends.

📁 src/app.js (Configures & Exports Express App)No app.listen()!
// src/app.js const express = require('express'); const cors = require('cors'); const userRoutes = require('./routes/userRoutes'); const orderRoutes = require('./routes/orderRoutes'); const { errorHandler } = require('./middleware/errorHandler'); const app = express(); // Global Middleware app.use(cors()); app.use(express.json()); // Mount Modular Routers app.use('/api/v1/users', userRoutes); app.use('/api/v1/orders', orderRoutes); // Centralized Error Handling Middleware (Express 5.x handles async errors!) app.use(errorHandler); module.exports = app;
📁 src/server.js (Starts HTTP Listener & Handles Shutdown)Entrypoint
// src/server.js const app = require('./app'); const { env } = require('./config/env'); const { connectDatabase } = require('./config/db'); async function bootstrap() { await connectDatabase(); const server = app.listen(env.PORT, () => { console.log(`🚀 Server running on port ${env.PORT} in ${env.NODE_ENV} mode`); }); // Graceful Shutdown Handler const shutdown = () => { console.log('Stopping HTTP server...'); server.close(() => { console.log('HTTP server closed. Exiting process.'); process.exit(0); }); }; process.on('SIGTERM', shutdown); process.on('SIGINT', shutdown); } bootstrap();
Why This Separation is Essential for Automated Testing

When testing with supertest(app), Supertest boots an ephemeral in-memory server without binding to port 3000. If app.listen() is embedded directly inside app.js, running tests will cause EADDRINUSE errors and prevent test suites from running concurrently.

SECTION 05

FastAPI Official Multi-File Structure

Explore FastAPI's official multi-file architecture ("Bigger Applications") using Python packages, APIRouter, and dependency injection.

app/
├── __init__.py # Makes "app" a Python package
├── main.py # Creates FastAPI app & includes routers
├── dependencies.py # Shared Depends() functions (get_db, auth)
├── config.py # Pydantic BaseSettings & env vars
├── routers/ # Modular APIRouter submodules
│ ├── __init__.py
│ ├── items.py
│ └── users.py
├── services/ # Business operations & math
├── models/ # SQLAlchemy database models
└── schemas/ # Pydantic validation DTOs
📁 app/main.py (Official FastAPI Assembly)FastAPI include_router()
# app/main.py from fastapi import FastAPI, Depends from .dependencies import get_query_token, get_token_header from .routers import items, users app = FastAPI(dependencies=[Depends(get_query_token)]) # Include modular sub-routers with dedicated URL prefixes app.include_router(users.router) app.include_router( items.router, prefix="/items", tags=["items"], dependencies=[Depends(get_token_header)], responses={404: {"description": "Not found"}}, ) @app.get("/") async def root(): return {"message": "Hello Bigger Applications!"}
SECTION 06

Interactive Refactoring Studio: Break the Monolith

Hands-on coding exercise: Reorganize a messy 1-file backend (monolithApp.js) into clean modular files: routes, service, repository, and application assembler.

Refactoring Workbench: Modular Architecture
Execution Output & Architectural Test TracerNODE.JS WORKSPACE
System Ready. Currently running monolithic single-file backend (monolithApp.js). Click 'Run Tests / Verify Structure' or 'Send Request (POST /api/orders)' to evaluate organization.
SECTION 07

Production Debugging: 7 Structural Anti-Patterns

Diagnose and resolve realistic structural bugs that plague growing engineering teams.

Scenario #1: Circular Dependency Between Services

`userService.js` imports `authService.js`, while `authService.js` imports `userService.js`.
Symptom: Runtime crash: `TypeError: authService.verifyToken is not a function` during server bootstrap.
Buggy Project Structure CodeFLAW DETECTED
// ❌ BUGGY: Circular import chain // services/userService.js const authService = require('./authService'); class UserService { async register(userData) { const hash = await authService.hashPassword(userData.password); // save user... } } module.exports = new UserService(); // services/authService.js const userService = require('./userService'); class AuthService { async login(email, password) { const user = await userService.findByEmail(email); // user is undefined during cyclic load! } } module.exports = new AuthService();
How should this structural issue be resolved?
SECTION 08

Architectural Challenge & 5 Golden Rules

Put yourself in the Lead Architect's shoes: choose between Layer-Based and Feature-Based structure for an e-commerce platform.

Architecture Decision Challenge

Scenario: You are architecting a new e-commerce backend with Users, Products, and Orders. Your engineering team consists of 8 full-time developers divided into two independent squads (Catalog Squad and Checkout Squad), releasing multiple features weekly.

Which structural philosophy should you adopt for the src/ directory?

5 Golden Rules of Backend Project Structure

RULE 01
Group by Change

Files that change together should live together. If editing an order always touches its route, service, and repo, consider feature slicing.

RULE 02
app.js vs server.js

Always separate application configuration (middleware & routes) from the physical HTTP port listener (app.listen).

RULE 03
Unidirectional Flow

Dependencies must point inward: Router → Controller → Service → Repository. Never let lower layers import upper layers.

RULE 04
No Circular Dependencies

Keep module relationships acyclic. If Service A and Service B need each other, extract the shared logic into a third utility or service.

RULE 05
Match Scale to Need

Evolve structure as complexity grows. Do not create 10 folders for a 2-endpoint prototype to look "enterprise".

SECTION 09

Mastery Assessment Quiz (7 Concept Questions)

Verify your deep understanding of backend project organization, module decoupling, and framework conventions.

QUESTION 1 OF 7Current Score: 0 / 7
Why does Express.js NOT mandate an official, rigid directory structure for backend applications?
Select your answer to unlock the next question
Backend Architecture Roadmap
Next Section: Validation & Error Handling
Continue to Validation