Shop Flow follows a Clean Architecture pattern with Domain-Driven Design (DDD) principles, implementing a modular monolithic structure. The project uses TypeScript, Express.js, and MongoDB with Mongoose as the ODM.
The project is organized into distinct layers with clear separation of concerns:
src/
├── app/ # Application Layer (Configuration & Setup)
├── core/ # Domain Layer (Business Logic)
├── middlewares/ # Infrastructure Layer (Cross-cutting concerns)
├── errors/ # Domain Layer (Custom Error Classes)
├── utils/ # Infrastructure Layer (Utilities)
└── __tests__/ # Test Layer
Each business domain is encapsulated in its own module with complete separation:
src/core/
├── users/ # User Domain
├── products/ # Product Domain
├── orders/ # Order Domain
├── reviews/ # Review Domain
├── uploads/ # Upload Domain
└── views/ # View Domain
Each domain implements the Repository pattern for data access abstraction:
// Example: UserRepository
export class UserRepository {
constructor(private readonly userModel: IUserModel) {}
async findById(userId: string): Promise<IUserDoc | null> {
return this.userModel.findById(userId);
}
async create(createUserDto: ICreateUserDto): Promise<IUserDoc> {
return this.userModel.create(createUserDto);
}
}The project uses manual dependency injection through a central index.ts file:
// Repositories Injection
export const userRepository = new UserRepository(User);
export const productRepository = new ProductRepository(Product);
// Services Injection
export const userService = new UserService(userRepository);
export const authService = new AuthService(userRepository);
// Controllers Injection
export const userController = new UserController(userService);
export const authController = new AuthController(authService);Purpose: Application configuration, setup, and bootstrapping
index.ts- Main application entry pointconfig.ts- Express middleware configurationroutes.ts- Route registrationdb.ts- Database connection setup
Key Features:
- Global error handling setup
- Security middleware configuration (Helmet, CORS, Rate Limiting)
- Database connection management
- Route registration and API documentation
Purpose: Business logic and domain entities
Each domain module follows the same structure:
domain/
├── domain.entity.ts # Mongoose schema and model
├── domain.interface.ts # TypeScript interfaces
├── domain.repository.ts # Data access layer
├── domain.service.ts # Business logic layer
├── domain.controller.ts # HTTP request handling
├── domain.routes.ts # Route definitions
└── dtos/ # Data Transfer Objects
├── create-domain.dto.ts
└── update-domain.dto.ts
- Mongoose Schemas: Define data structure and validation
- Instance Methods: Business logic methods attached to documents
- Middleware: Pre/post hooks for data processing
// Example: User entity with business methods
userSchema.methods.signToken = function (): string {
return jwt.sign({ id: this._id }, process.env.JWT_SECRET!);
};
userSchema.methods.correctPassword = async function (
candidatePassword: string,
) {
return await bcrypt.compare(candidatePassword, this.password);
};- CRUD Operations: Create, Read, Update, Delete
- Query Building: Complex database queries
- Data Aggregation: Statistical and analytical queries
- Business Logic: Core application logic
- Validation: Business rule validation
- Error Handling: Domain-specific error management
- HTTP Handling: Request/response processing
- Input Validation: Request data validation
- Response Formatting: Consistent API responses
Cross-cutting concerns implemented as Express middleware:
auth.ts- Authentication and authorizationerror-handler.ts- Global error handlingvalidate-request.ts- Request validationsecurity.ts- Security utilitiesupload.ts- File upload handling
Custom error classes extending a base CustomError:
NotFoundError- 404 errorsBadRequestError- 400 errorsNotAuthorizedError- 401 errorsForbiddenError- 403 errors
Used in the dependency injection setup in src/core/index.ts
Implemented in API features utility for different query operations
Express.js middleware chain for request processing
Mongoose middleware hooks for document lifecycle events
Base repository and service classes with common operations
- Each domain is self-contained
- Clear boundaries between modules
- Easy to extract into microservices if needed
- Separate interfaces for different concerns
- DTOs for data transfer
- Clear contracts between layers
- Each class has one reason to change
- Separation of concerns across layers
- Focused, cohesive modules
- High-level modules don't depend on low-level modules
- Both depend on abstractions (interfaces)
- Dependency injection for loose coupling
domain.entity.ts- Mongoose model and schemadomain.interface.ts- TypeScript interfacesdomain.repository.ts- Data access layerdomain.service.ts- Business logic layerdomain.controller.ts- HTTP handlersdomain.routes.ts- Route definitions
create-domain.dto.ts- Creation payloadupdate-domain.dto.ts- Update payloaddomain-specific.dto.ts- Specialized DTOs
domain.spec.ts- Unit testsdomain-integration.spec.ts- Integration tests
Centralized configuration through environment variables:
# Database
MONGO_URI=mongodb://localhost:27017/shop-flow
# JWT Configuration
JWT_SECRET=your_access_token_secret
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=your_refresh_token_secret
JWT_REFRESH_EXPIRES_IN=7d
# Security
NODE_ENV=developmentConfiguration is centralized in src/app/config.ts:
- Security headers (Helmet)
- CORS configuration
- Rate limiting
- Body parsing
- Cookie parsing
- Data sanitization
- Normalization: Related data in separate collections
- Embedding: Small, frequently accessed data embedded
- Indexing: Strategic indexes for query performance
- Validation: Schema-level and application-level validation
- Users ↔ Orders: One-to-Many
- Products ↔ Reviews: One-to-Many
- Orders ↔ Products: Many-to-Many (through order items)
- Users ↔ Reviews: One-to-Many
- JWT-based authentication
- Refresh token rotation
- Secure cookie storage
- Multi-layer token validation
- Role-based access control (RBAC)
- Route-level permissions
- Resource-level permissions
- Input sanitization
- XSS protection
- NoSQL injection prevention
- Rate limiting
- HTTPS enforcement
src/__tests__/
├── helpers/ # Test utilities
├── integration/ # Integration tests
│ ├── auth/ # Authentication tests
│ ├── users/ # User domain tests
│ ├── products/ # Product domain tests
│ └── orders/ # Order domain tests
└── setup.ts # Test configuration
- Integration Tests: End-to-end API testing
- Test Helpers: Reusable test utilities
- Database Isolation: In-memory MongoDB for tests
- Authentication Helpers: Token generation utilities
- OpenAPI 3.0 specification
- Interactive API documentation
- Automatic schema generation
- Request/response examples
src/swagger/
├── config.ts # Swagger configuration
├── base.json # Base OpenAPI specification
└── apis/ # API endpoint documentation
├── admin.yaml
├── auth.yaml
└── products.yaml
- Strategic indexing
- Query optimization
- Connection pooling
- Aggregation pipelines
- Async/await patterns
- Error handling with express-async-errors
- Efficient middleware ordering
- Memory management
- Domain-First: Start with domain entities
- Repository Layer: Implement data access
- Service Layer: Add business logic
- Controller Layer: Handle HTTP requests
- Route Registration: Wire up endpoints
- TypeScript: Strong typing throughout
- Error Handling: Consistent error responses
- Validation: Input validation at multiple layers
- Documentation: Comprehensive API documentation
- Testing: Test-driven development approach