This document outlines the major refactoring performed on the Skill Tree project and establishes best practices for future development.
The project has transitioned from a monolithic app.js to a modular, layered architecture located in the src/ directory.
src/
├── app.js # Express application configuration
├── server.js # Entry point (DB connection & Server start)
├── config/ # Centralized configuration & DB logic
├── controllers/ # Business logic and request handling
├── middleware/ # Express middleware (Auth, Validation)
├── models/ # Mongoose schemas and models
├── routes/ # Router definitions and endpoint mapping
└── utils/ # Shared helper functions and logic
- Logic extraction: Business logic previously embedded in route handlers has been moved to Controllers.
- Route Modularization: Endpoints are grouped by domain (e.g.,
/auth,/user,/admin) in separate files withinsrc/routes/. - Centralized Middleware: Authentication logic is now a reusable middleware in
src/middleware/auth.js.
- Async/Await: Replaced legacy Mongoose callback patterns with
async/awaitto improve readability and compatibility with Mongoose 8. - Error Handling: Standardized
try/catchblocks in controllers to manage asynchronous errors gracefully.
server.jsvsapp.js: Separated the application setup (routes, middleware) from the actual server start-up (listening on a port). This is a best practice for easier testing.
Controllers should primarily handle request parsing and response orchestration. Complex business logic should be delegated to utilities or service layers.
Validation, authentication, and logging should always be handled via Express middleware to keep route handlers focused on their specific tasks.
Always use src/config/config.js or environment variables (via dotenv) for secrets and connection strings. Never hardcode sensitive values in the logic.
Follow the established pattern of returning a consistent JSON structure for both success and error cases:
{
"success": boolean,
"message": "Human readable message",
"data": { ... }
}When creating utilities, ensure they are independent of the request/response cycle where possible, making them easier to unit test.