Routes Layer
- Define endpoints
- Apply middleware
- Route to controllers
```typescript
// routes/users.routes.ts
import { Router } from 'express';
import { UserController } from '../controllers/user.controller';
import { validateRequest } from '../middleware/validate';
import { createUserSchema, updateUserSchema } from '../validators/user.validator';
const router = Router();
const controller = new UserController();
router.get('/', controller.getAll);
router.get('/:id', controller.getById);
router.post('/', validateRequest(createUserSchema), controller.create);
router.put('/:id', validateRequest(updateUserSchema), controller.update);
router.delete('/:id', controller.delete);
export default router;
```
Controllers Layer
- Handle HTTP request/response
- Extract and validate input
- Call services
- Return responses
```typescript
// controllers/user.controller.ts
import { Request, Response, NextFunction } from 'express';
import { UserService } from '../services/user.service';
export class UserController {
private userService = new UserService();
getAll = async (req: Request, res: Response, next: NextFunction) => {
try {
const users = await this.userService.findAll();
res.json({ data: users });
} catch (error) {
next(error);
}
};
getById = async (req: Request, res: Response, next: NextFunction) => {
try {
const { id } = req.params;
const user = await this.userService.findById(id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json({ data: user });
} catch (error) {
next(error);
}
};
create = async (req: Request, res: Response, next: NextFunction) => {
try {
const user = await this.userService.create(req.body);
res.status(201).json({ data: user });
} catch (error) {
next(error);
}
};
}
```
Services Layer
- Business logic
- Orchestrate operations
- Transaction management
```typescript
// services/user.service.ts
import { UserRepository } from '../repositories/user.repository';
import { CreateUserDto, UpdateUserDto } from '../types/user.types';
import { AppError } from '../utils/errors';
export class UserService {
private userRepository = new UserRepository();
async findAll() {
return this.userRepository.findAll();
}
async findById(id: string) {
return this.userRepository.findById(id);
}
async create(data: CreateUserDto) {
// Business logic
const existingUser = await this.userRepository.findByEmail(data.email);
if (existingUser) {
throw new AppError('Email already exists', 409);
}
// Hash password, etc.
const hashedPassword = await hashPassword(data.password);
return this.userRepository.create({
...data,
password: hashedPassword,
});
}
async update(id: string, data: UpdateUserDto) {
const user = await this.userRepository.findById(id);
if (!user) {
throw new AppError('User not found', 404);
}
return this.userRepository.update(id, data);
}
}
```
Repositories Layer
- Database operations
- Query building
- Data mapping
```typescript
// repositories/user.repository.ts
import { prisma } from '../config/database';
import { User, CreateUserInput, UpdateUserInput } from '../types/user.types';
export class UserRepository {
async findAll(): Promise {
return prisma.user.findMany({
select: {
id: true,
email: true,
name: true,
createdAt: true,
},
});
}
async findById(id: string): Promise {
return prisma.user.findUnique({
where: { id },
});
}
async findByEmail(email: string): Promise {
return prisma.user.findUnique({
where: { email },
});
}
async create(data: CreateUserInput): Promise {
return prisma.user.create({
data,
});
}
async update(id: string, data: UpdateUserInput): Promise {
return prisma.user.update({
where: { id },
data,
});
}
async delete(id: string): Promise {
await prisma.user.delete({
where: { id },
});
}
}
```