Skip to content

SandeshR98/nodejs-clean-architecture

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Clean Architecture with Node.js & TypeScript

Node.js TypeScript MongoDB License: MIT

A production-ready REST API built with Node.js, TypeScript, and Clean Architecture principles. This project demonstrates how to create a maintainable, scalable, and testable backend application with proper separation of concerns.

🎯 Key Features

  • βœ… Clean Architecture - Clear separation of concerns across 4 layers
  • βœ… Database-Agnostic - Easy to switch from MongoDB to PostgreSQL, MySQL, etc.
  • βœ… TypeScript - Full type safety with strict mode enabled
  • βœ… Repository Pattern - Abstracted data access layer
  • βœ… Dependency Injection - Manual DI using factory pattern
  • βœ… Validation - Input validation using Joi schemas
  • βœ… Error Handling - Centralized error handling middleware
  • βœ… Logging - Structured logging with Winston
  • βœ… Security - Helmet, CORS, and security best practices
  • βœ… Production-Ready - Optimized for deployment

πŸ“‹ Table of Contents

πŸ—οΈ Architecture

This project follows Clean Architecture principles with 4 distinct layers:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚          Interfaces Layer (HTTP)                β”‚
β”‚   Controllers β†’ Routes β†’ Middleware             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”˜
                   β”‚                            β”‚
                   β–Ό                            β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β”‚
β”‚      Application Layer (Business Logic)       β”‚
β”‚             Services                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚
                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Domain Layer (Core Business)        β”‚
β”‚   Entities β†’ Repository Interfaces          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β–²
                   β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚     Infrastructure Layer (External I/O)     β”‚
β”‚   Database β†’ Email β†’ File Storage           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Layer Responsibilities

1. Domain Layer (Core)

  • Contains business entities and repository interfaces
  • No dependencies on other layers or external frameworks
  • Pure TypeScript interfaces defining the domain model

2. Application Layer

  • Contains business logic in services
  • Depends only on the domain layer
  • Orchestrates use cases using domain repositories

3. Interface Layer

  • Contains HTTP controllers, routes, and middleware
  • Depends on application and domain layers
  • Translates between HTTP requests/responses and domain concepts

4. Infrastructure Layer

  • Contains implementation details (database, email, etc.)
  • Depends on domain interfaces
  • Implements repository interfaces with concrete technology

πŸ“ Project Structure

src/
β”œβ”€β”€ config/                     # Configuration files
β”‚   β”œβ”€β”€ database.ts            # Database connection
β”‚   β”œβ”€β”€ logger.ts              # Winston logger
β”‚   └── env.ts                 # Environment variables
β”‚
β”œβ”€β”€ domain/                     # Domain Layer (Core)
β”‚   β”œβ”€β”€ entities/
β”‚   β”‚   β”œβ”€β”€ IBaseEntity.ts     # Base entity interface
β”‚   β”‚   └── IUser.ts           # User entity
β”‚   β”œβ”€β”€ repositories/
β”‚   β”‚   β”œβ”€β”€ IGenericRepository.ts  # Generic CRUD interface
β”‚   β”‚   └── IUserRepository.ts     # User repository interface
β”‚   β”œβ”€β”€ services/
β”‚   β”‚   └── IUserService.ts        # User service interface
β”‚   └── shared/
β”‚       β”œβ”€β”€ ApiResponse.ts         # Standard API response
β”‚       └── AppError.ts            # Custom error class
β”‚
β”œβ”€β”€ application/                # Application Layer
β”‚   β”œβ”€β”€ request-dtos/
β”‚   β”‚   └── IUserRequest.ts        # User request DTOs
β”‚   └── services/
β”‚       └── UserService.ts         # User business logic
β”‚
β”œβ”€β”€ interfaces/                 # Interface Layer
β”‚   └── http/
β”‚       β”œβ”€β”€ controllers/
β”‚       β”‚   └── UserController.ts  # HTTP handlers
β”‚       β”œβ”€β”€ middlewares/
β”‚       β”‚   └── ErrorMiddleware.ts # Error handling
β”‚       β”œβ”€β”€ routes/
β”‚       β”‚   └── UserRoutes.ts      # Route definitions
β”‚       └── validation/
β”‚           β”œβ”€β”€ schemas/
β”‚           β”‚   └── UserSchemas.ts # Joi validation
β”‚           └── validator.ts       # Validation middleware
β”‚
β”œβ”€β”€ infrastructure/             # Infrastructure Layer
β”‚   └── database/
β”‚       └── mongodb/
β”‚           β”œβ”€β”€ models/
β”‚           β”‚   β”œβ”€β”€ IBaseDocument.ts   # Base Mongoose doc
β”‚           β”‚   └── UserModel.ts       # User Mongoose model
β”‚           └── repositories/
β”‚               β”œβ”€β”€ BaseRepository.ts  # Generic repo implementation
β”‚               └── UserRepository.ts  # User repo implementation
β”‚
β”œβ”€β”€ app.ts                      # Express app setup
β”œβ”€β”€ server.ts                   # HTTP server
β”œβ”€β”€ container.ts                # Dependency injection
β”œβ”€β”€ bootstrap.ts                # Application bootstrap
└── index.ts                    # Entry point

πŸš€ Getting Started

Prerequisites

  • Node.js >= 20.x
  • MongoDB >= 6.x (or Docker)
  • npm or yarn

Installation

  1. Clone the repository
git clone https://github.com/yourusername/clean-architecture-nodejs.git
cd clean-architecture-nodejs
  1. Install dependencies
npm install
  1. Setup environment variables
cp .env.example .env
# Edit .env with your configuration
  1. Start MongoDB (if using Docker)
docker run -d -p 27017:27017 --name mongodb mongo:latest
  1. Run the application
# Development mode
npm run dev

# Production build
npm run build
npm start

The server will start on http://localhost:3000

πŸ”Œ API Endpoints

User Management

Register User

POST /api/users/register
Content-Type: application/json

{
  "firstName": "John",
  "lastName": "Doe",
  "email": "john.doe@example.com",
  "password": "Password@123",
  "confirmPassword": "Password@123",
  "phoneNumber": "+1234567890"
}

Get All Users

GET /api/users?page=1&limit=10

Get User By ID

GET /api/users/:id

Update User

PUT /api/users/:id
Content-Type: application/json

{
  "firstName": "Jane",
  "phoneNumber": "+9876543210"
}

Delete User

DELETE /api/users/:id

Health Check

GET /health

Response:

{
  "status": "OK",
  "timestamp": "2025-01-15T10:30:00.000Z",
  "uptime": 123.456
}

🌐 Environment Variables

Create a .env file in the root directory:

# Application
NODE_ENV=development
PORT=3000

# Database
MONGODB_URI=mongodb://localhost:27017/clean-architecture-db

# Logging
LOG_LEVEL=info

# JWT (for future authentication)
JWT_SECRET=your-super-secret-key-change-in-production
JWT_EXPIRES_IN=7d

# CORS
CORS_ORIGIN=*

πŸ’» Development

Available Scripts

# Development with hot reload
npm run dev

# Development with tsx (faster)
npm run dev:tsx

# Build for production
npm run build

# Start production server
npm start

# Type checking
npm run compile

# Linting
npm run lint

# Format code
npm run format

Code Style

This project uses:

  • ESLint for linting
  • Prettier for code formatting
  • TypeScript strict mode for type safety

🏭 Production Build

Build the project for production:

npm run build

This will:

  1. Compile TypeScript to JavaScript
  2. Resolve path aliases using tsc-alias
  3. Output to the dist/ directory

Run in production:

npm start

Docker Deployment

Create a Dockerfile:

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

EXPOSE 3000

CMD ["node", "dist/index.js"]

Build and run:

docker build -t clean-architecture-api .
docker run -p 3000:3000 --env-file .env clean-architecture-api

πŸ§ͺ Testing the API

Using cURL

Register a user:

curl -X POST http://localhost:3000/api/users/register \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "John",
    "lastName": "Doe",
    "email": "john@example.com",
    "password": "Password@123",
    "confirmPassword": "Password@123"
  }'

Get all users:

curl http://localhost:3000/api/users?page=1&limit=10

Using Postman

Import the following collection:

{
  "info": {
    "name": "Clean Architecture API",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "Register User",
      "request": {
        "method": "POST",
        "header": [],
        "body": {
          "mode": "raw",
          "raw": "{\n  \"firstName\": \"John\",\n  \"lastName\": \"Doe\",\n  \"email\": \"john@example.com\",\n  \"password\": \"Password@123\",\n  \"confirmPassword\": \"Password@123\"\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "url": {
          "raw": "http://localhost:3000/api/users/register",
          "protocol": "http",
          "host": ["localhost"],
          "port": "3000",
          "path": ["api", "users", "register"]
        }
      }
    }
  ]
}

πŸ”„ Database-Agnostic Design

This architecture is completely database-agnostic. The business logic has zero knowledge of MongoDB, PostgreSQL, or any specific database.

How to Switch Databases

Example: Adding PostgreSQL Support

  1. Install TypeORM and PostgreSQL driver:
npm install typeorm pg
  1. Create PostgreSQL Repository:
// src/infrastructure/database/postgresql/repositories/UserRepository.ts
import { IUserRepository } from '@domain/repositories/IUserRepository';
import { Repository } from 'typeorm';

export class PostgresUserRepository implements IUserRepository {
  constructor(private repository: Repository<User>) {}
  
  async create(data: Partial<IUser>): Promise<IUser> {
    const user = this.repository.create(data);
    return await this.repository.save(user);
  }
  
  // ... implement all interface methods
}
  1. Update Factory:
// src/infrastructure/database/factory.ts
export function createUserRepository(dbType: string): IUserRepository {
  switch (dbType) {
    case 'mongodb':
      return new MongoUserRepository();
    case 'postgresql':
      return new PostgresUserRepository(getTypeORMRepo());
    default:
      throw new Error(`Unsupported database: ${dbType}`);
  }
}
  1. Update Environment:
DATABASE_TYPE=postgresql
DATABASE_URL=postgresql://user:pass@localhost:5432/db

That's it! Your business logic remains completely unchanged.

Supported ORMs

You can use any ORM you prefer:

  • Mongoose (MongoDB) - Current implementation
  • TypeORM (PostgreSQL, MySQL, SQLite, etc.)
  • Prisma (PostgreSQL, MySQL, SQLite, etc.)
  • Sequelize (PostgreSQL, MySQL, MariaDB, etc.)

The Repository Pattern ensures your business logic stays clean regardless of your database choice.

🎨 Design Patterns Used

  • Clean Architecture - Separation of concerns
  • Repository Pattern - Data access abstraction
  • Dependency Injection - Loose coupling
  • Factory Pattern - Object creation
  • DTO Pattern - Data transfer objects
  • Middleware Pattern - Request/response processing

πŸ“š Key Takeaways

  1. Separation of Concerns - Each layer has a single responsibility
  2. Dependency Inversion - High-level modules don't depend on low-level modules
  3. Database Independence - Easy to switch databases
  4. Testability - Business logic is isolated and easily testable
  5. Maintainability - Clear structure makes code easy to understand
  6. Scalability - Well-organized for growing codebases

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the project
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

πŸ“ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

  • Clean Architecture by Robert C. Martin (Uncle Bob)
  • Node.js Best Practices
  • TypeScript Community

πŸ“§ Contact

Project Link: https://github.com/sandeshR98/nodejs-clean-architecture


⭐ If you find this project helpful, please give it a star!

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published