210 lines
5.5 KiB
Markdown
210 lines
5.5 KiB
Markdown
# Clean Architecture - Backend OptiHack
|
|
|
|
## Estructura del Proyecto
|
|
|
|
```
|
|
src/
|
|
├── config/ # Configuración e integración con librerías
|
|
│ ├── env.ts # Variables de entorno
|
|
│ ├── jwt.ts # JwtAdapter - genera y valida tokens
|
|
│ └── bcrypt.ts # BcryptAdapter - hashea y compara contraseñas
|
|
│
|
|
├── domain/ # Lógica de negocio (sin dependencias externas)
|
|
│ ├── dtos/ # Data Transfer Objects con validación
|
|
│ │ ├── auth/
|
|
│ │ │ ├── register-user.dto.ts
|
|
│ │ │ └── login-user.dto.ts
|
|
│ │ └── index.ts
|
|
│ │
|
|
│ ├── repositories/ # Interfaces de repositorios (contratos)
|
|
│ │ └── auth.repository.ts
|
|
│ │
|
|
│ ├── use-cases/ # Lógica de negocio (casos de uso)
|
|
│ │ └── auth/
|
|
│ │ ├── register-user.use-case.ts
|
|
│ │ ├── login-user.use-case.ts
|
|
│ │ └── get-me.use-case.ts
|
|
│ │
|
|
│ └── errors/ # Errores personalizados
|
|
│ └── custom.error.ts
|
|
│
|
|
├── data/ # Implementación de datos (Prisma, BD)
|
|
│ ├── postgres/ # Cliente de Prisma
|
|
│ │ └── index.ts
|
|
│ └── repositories/ # Implementación de repositorios
|
|
│ └── auth.repository.impl.ts
|
|
│
|
|
├── presentation/ # HTTP (Express, controladores, middlewares)
|
|
│ ├── auth/
|
|
│ │ ├── controller.ts
|
|
│ │ └── routes.ts
|
|
│ ├── middlewares/
|
|
│ │ └── auth.middleware.ts
|
|
│ ├── routes.ts # Rutas principales
|
|
│ └── server.ts # Instancia de Express
|
|
│
|
|
├── app.ts # Punto de entrada
|
|
└── ...
|
|
```
|
|
|
|
## Flujo de Datos (Regla Principal)
|
|
|
|
```
|
|
HTTP Request
|
|
↓
|
|
Controller recibe los datos
|
|
↓
|
|
Controller llama al UseCase
|
|
↓
|
|
UseCase hace la lógica de negocio
|
|
↓
|
|
UseCase llama al Repository
|
|
↓
|
|
Repository implementación habla con Prisma
|
|
↓
|
|
Prisma ejecuta queries en la BD
|
|
↓
|
|
Response va hacia arriba
|
|
↓
|
|
HTTP Response
|
|
```
|
|
|
|
### Ejemplo: Register
|
|
|
|
1. **Cliente** envía POST `/api/auth/register` con `{ name, email, password }`
|
|
2. **Controller** recibe la request
|
|
3. **Controller** llama a `RegisterUserUseCase.execute()`
|
|
4. **UseCase** valida el DTO
|
|
5. **UseCase** verifica si el email existe (llama al Repository)
|
|
6. **Repository** busca en la BD
|
|
7. **UseCase** hashea la contraseña
|
|
8. **UseCase** crea el usuario (llama al Repository)
|
|
9. **Repository** inserta en la BD
|
|
10. **UseCase** genera el JWT
|
|
11. **Controller** responde con user + token
|
|
|
|
## Responsabilidades por Capa
|
|
|
|
### **Config**
|
|
- Variables de entorno
|
|
- Adapters (JWT, Bcrypt)
|
|
- Configuraciones globales
|
|
|
|
### **Domain** (Sin dependencias externas)
|
|
- DTOs con validación
|
|
- Interfaces de repositorios (contratos)
|
|
- Casos de uso (lógica de negocio)
|
|
- Errores personalizados
|
|
|
|
### **Data**
|
|
- Implementación de repositorios
|
|
- Cliente de Prisma
|
|
- Queries a la base de datos
|
|
|
|
### **Presentation**
|
|
- Controladores (Express handlers)
|
|
- Rutas
|
|
- Middlewares
|
|
- Validación HTTP
|
|
|
|
## Endpoints de Autenticación
|
|
|
|
### 1. Register
|
|
```
|
|
POST /api/auth/register
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"name": "John Doe",
|
|
"email": "john@example.com",
|
|
"password": "password123"
|
|
}
|
|
|
|
Response 201:
|
|
{
|
|
"user": {
|
|
"id": 1,
|
|
"email": "john@example.com",
|
|
"name": "John Doe"
|
|
},
|
|
"token": "eyJhbGc..."
|
|
}
|
|
```
|
|
|
|
### 2. Login
|
|
```
|
|
POST /api/auth/login
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"email": "john@example.com",
|
|
"password": "password123"
|
|
}
|
|
|
|
Response 200:
|
|
{
|
|
"user": {
|
|
"id": 1,
|
|
"email": "john@example.com",
|
|
"name": "John Doe"
|
|
},
|
|
"token": "eyJhbGc..."
|
|
}
|
|
```
|
|
|
|
### 3. GetMe (Protegida)
|
|
```
|
|
GET /api/auth/me
|
|
Authorization: Bearer eyJhbGc...
|
|
|
|
Response 200:
|
|
{
|
|
"id": 1,
|
|
"email": "john@example.com",
|
|
"name": "John Doe",
|
|
"role": "USER"
|
|
}
|
|
```
|
|
|
|
## Validaciones
|
|
|
|
### DTOs
|
|
- **name**: string no vacío
|
|
- **email**: email válido (regex)
|
|
- **password**: mínimo 6 caracteres
|
|
|
|
### Errores
|
|
- **400 Bad Request**: Datos inválidos
|
|
- **401 Unauthorized**: Token inválido, usuario no autenticado
|
|
- **404 Not Found**: Usuario no encontrado
|
|
- **409 Conflict**: Email ya en uso
|
|
- **500 Internal Server Error**: Error del servidor
|
|
|
|
## Variables de Entorno (.env)
|
|
|
|
```
|
|
PORT=3000 # Puerto del servidor
|
|
NODE_ENV=development # Entorno
|
|
DATABASE_URL=postgresql://user:pass@host:5432/db # URL de BD
|
|
JWT_SEED=tu_secreto_super_seguro # Seed para JWT
|
|
JWT_EXPIRES_IN=7d # Expiración del token
|
|
BCRYPT_ROUNDS=10 # Rondas de bcrypt
|
|
```
|
|
|
|
## Cómo Agregar un Nuevo UseCase
|
|
|
|
1. **Crear el UseCase** en `domain/use-cases/auth/new-feature.use-case.ts`
|
|
2. **Agregar método al Repository** en `domain/repositories/auth.repository.ts`
|
|
3. **Implementar en** `data/repositories/auth.repository.impl.ts`
|
|
4. **Crear método en Controller** en `presentation/auth/controller.ts`
|
|
5. **Agregar ruta** en `presentation/auth/routes.ts`
|
|
|
|
## Ventajas de Esta Arquitectura
|
|
|
|
✓ **Separación de responsabilidades**: Cada capa hace una cosa
|
|
✓ **Fácil de testear**: UseCase no conoce HTTP ni BD
|
|
✓ **Mantenible**: Cambios en BD no afectan lógica de negocio
|
|
✓ **Escalable**: Fácil agregar nuevos módulos
|
|
✓ **Simple**: No over-engineered, ideal para MVP/Hackathon
|
|
✓ **Limpio**: Código organizado y fácil de entender
|