# CLAUDE.md

Este archivo proporciona orientación a Claude Code (claude.ai/code) para trabajar con el código de este repositorio.

## Contexto

**Prueba técnica** para posición de **Desarrollador Master .NET/Angular** en **Inter Rapidísimo**.
- Cargo: Desarrollador Senior .NET/Angular
- Empresa: Inter Rapidísimo (logística y mensajería)
- Ubicación: Bogotá, Av. El Dorado (presencial)

---

## Credenciales y Autenticación

> **CRÍTICO:** Antes de cualquier operación que requiera autenticación, revisar `~/.secrets/credentials.env`

**Archivo de credenciales:** `~/.secrets/credentials.env`

### Uso Obligatorio (SIEMPRE)

```bash
source ~/.secrets/credentials.env
```

### Variables Disponibles

| Variable | Uso |
|----------|-----|
| `GIT_USER` | Usuario Git para Gitea (andresgarcia0313) |
| `GIT_PASS` | Contraseña Git para Gitea |
| `GIT_REMOTE_URL` | URL base con credenciales embebidas |
| `GITEA_URL` | URL del servidor Gitea |
| `GITEA_ADMIN_USER` | Usuario API Gitea |
| `GITEA_ADMIN_PASS` | Contraseña API Gitea |
| `K8S_MASTER_HOST` | Host del master K3s |
| `K8S_SUDO_PASS` | Password sudo en K3s |

### Login Automático (OBLIGATORIO)

**Git Push/Pull a Gitea:**
```bash
source ~/.secrets/credentials.env
# Configurar remote con credenciales
git remote set-url gitea "${GIT_REMOTE_URL}/andresgarcia0313/academia.git"
# O directamente
git push gitea main
```

**API Gitea:**
```bash
source ~/.secrets/credentials.env
curl -u "$GITEA_ADMIN_USER:$GITEA_ADMIN_PASS" "$GITEA_URL/api/v1/user/repos"
```

**K3s (SSH):**
```bash
source ~/.secrets/credentials.env
ssh $K8S_MASTER_HOST "echo $K8S_SUDO_PASS | sudo -S kubectl get pods -A"
```

> **REGLA:** SIEMPRE autenticarse automáticamente usando `~/.secrets/credentials.env`. NUNCA preguntar al usuario por credenciales. Si falla la autenticación, revisar primero el archivo de credenciales.

---

## Resumen del Proyecto

**Aplicación web de registro de estudiantes** - Sistema CRUD con reglas de negocio específicas.

## Stack Tecnológico

| Capa | Tecnología |
|------|------------|
| **Backend** | .NET 10 (C#) |
| **API** | GraphQL (HotChocolate) |
| **Frontend** | Angular 21 (TypeScript) |
| **Base de datos** | SQL Server / SQLite (dev) |
| **ORM** | Entity Framework Core |
| **Autenticación** | JWT (HMAC-SHA256) + PBKDF2 |
| **Validación** | FluentValidation |
| **Mapping** | Mapster |
| **UI** | Angular Material |
| **GraphQL Client** | Apollo Angular |
| **CI/CD** | Gitea Actions + K3s |

---

## Requisitos de Negocio

### Funcionalidad Principal
- CRUD completo para inscripción de estudiantes
- Estudiantes se inscriben en programa basado en créditos
- Visualización de inscripciones de otros estudiantes (solo nombres en clases compartidas)

### Reglas del Dominio (CRÍTICAS)
- **10 asignaturas** en total, cada una vale **3 créditos**
- **5 profesores**, cada uno imparte **exactamente 2 asignaturas**
- Estudiantes pueden seleccionar **máximo 3 asignaturas** (= 9 créditos)
- **Restricción clave:** Un estudiante NO puede tener clases con el mismo profesor

---

## Arquitectura

### Clean Architecture + CQRS (4 Capas)

```
┌─────────────────────────────────────────────────────────────────┐
│                      HOST (Composition Root)                     │
│                 Program.cs / DI / Configuration                  │
└──────────────────────────────┬──────────────────────────────────┘
                               │
┌──────────────────────────────▼──────────────────────────────────┐
│                          ADAPTERS                                │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  DRIVING (Primary)          │  DRIVEN (Secondary)       │    │
│  │  ─────────────────          │  ──────────────────       │    │
│  │  GraphQL API ◄──Angular SPA │  Persistence (EF Core)   │    │
│  │  (HotChocolate)             │  DataLoaders             │    │
│  │  Middleware                 │  External Services       │    │
│  └─────────────────────────────────────────────────────────┘    │
└──────────────────────────────┬──────────────────────────────────┘
                               │
┌──────────────────────────────▼──────────────────────────────────┐
│                        APPLICATION                               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Commands / Queries / Handlers / DTOs / Validators      │    │
│  └─────────────────────────────────────────────────────────┘    │
└──────────────────────────────┬──────────────────────────────────┘
                               │
┌──────────────────────────────▼──────────────────────────────────┐
│                          DOMAIN                                  │
│  ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────────┐   │
│  │ Entities  │ │  Value    │ │ Domain    │ │    Ports      │   │
│  │           │ │  Objects  │ │ Services  │ │  (Contracts)  │   │
│  └───────────┘ └───────────┘ └───────────┘ └───────────────┘   │
└─────────────────────────────────────────────────────────────────┘
```

### Regla de Dependencia (INVIOLABLE)

```
Host → Adapters → Application → Domain
          │                        ▲
          └── implementa Ports ────┘

• Domain define Ports (interfaces)
• Adapters/Driven implementa Ports
• Domain NO depende de NADA externo
```

### Nomenclatura Ports & Adapters

| Término | Significado | Ubicación |
|---------|-------------|-----------|
| **Ports** | Contratos/Interfaces | `Domain/Ports/` |
| **Driving Adapters** | Entrada (quien llama) | `Adapters/Driving/` |
| **Driven Adapters** | Salida (a quien se llama) | `Adapters/Driven/` |

### Diagramas UML

Ubicación: `docs/architecture/diagrams/`

| Diagrama | Archivo | Propósito |
|----------|---------|-----------|
| Casos de Uso | `01-use-cases.puml` | Funcionalidades del estudiante |
| Modelo de Dominio | `02-domain-model.puml` | Entidades y Value Objects |
| Secuencia | `03-sequence-enrollment.puml` | Flujo de inscripción |
| Componentes | `04-components.puml` | Arquitectura del sistema |
| Entidad-Relación | `05-entity-relationship.puml` | Modelo de base de datos |
| Estados | `06-state-enrollment.puml` | Estados de inscripción |
| Despliegue | `07-deployment.puml` | Infraestructura Docker |
| C4 Contexto | `08-c4-context.puml` | Vista de alto nivel |

Regenerar SVG: `cat archivo.puml | plantuml -tsvg -pipe > archivo.svg`

---

## Estructura del Proyecto

```
/
├── src/
│   │
│   │  ═══════════════ BACKEND (.NET 10) ═══════════════
│   │
│   ├── backend/
│   │   │
│   │   ├── Domain/                    # ENTITIES (Núcleo puro)
│   │   │   ├── Entities/              # Student, Subject, Professor, Enrollment, User
│   │   │   ├── ValueObjects/          # Email
│   │   │   ├── ReadModels/            # ClassmateInfo
│   │   │   ├── Exceptions/            # DomainException
│   │   │   ├── Ports/
│   │   │   │   └── Repositories/      # IStudentRepository, IUserRepository, etc.
│   │   │   └── Services/              # EnrollmentDomainService
│   │   │
│   │   ├── Application/               # USE CASES (CQRS)
│   │   │   ├── Common/
│   │   │   │   ├── Behaviors/         # ValidationBehavior
│   │   │   │   └── ValidationPatterns.cs  # Regex patterns centralizados
│   │   │   ├── Auth/                  # Autenticación
│   │   │   │   ├── Commands/          # LoginCommand, RegisterCommand, ResetPasswordCommand
│   │   │   │   ├── DTOs/              # AuthDtos (LoginRequest, RegisterRequest, etc.)
│   │   │   │   ├── IJwtService.cs     # Interfaz JWT
│   │   │   │   ├── IPasswordService.cs # Interfaz hashing
│   │   │   │   └── JwtOptions.cs      # Configuración JWT
│   │   │   ├── Students/
│   │   │   │   ├── Commands/          # CreateStudent, UpdateStudent, DeleteStudent
│   │   │   │   ├── Queries/           # GetStudents, GetStudentById, GetStudentsPaged
│   │   │   │   └── DTOs/              # StudentDto
│   │   │   ├── Subjects/
│   │   │   │   ├── Queries/           # GetSubjects, GetAvailableSubjects
│   │   │   │   └── DTOs/              # SubjectDto
│   │   │   ├── Professors/
│   │   │   │   ├── Queries/           # GetProfessors
│   │   │   │   └── DTOs/              # ProfessorDto
│   │   │   ├── Enrollments/
│   │   │   │   ├── Commands/          # EnrollStudent, UnenrollStudent
│   │   │   │   ├── Queries/           # GetClassmates
│   │   │   │   └── DTOs/              # EnrollmentDtos
│   │   │   └── DependencyInjection.cs
│   │   │
│   │   ├── Adapters/                  # INTERFACE ADAPTERS
│   │   │   │
│   │   │   ├── Driving/               # Entrada (Primary)
│   │   │   │   └── Api/               # GraphQL (HotChocolate)
│   │   │   │       ├── Types/
│   │   │   │       │   ├── Auth/          # AuthMutations, AuthQueries
│   │   │   │       │   ├── Students/      # StudentType
│   │   │   │       │   ├── Subjects/      # SubjectType
│   │   │   │       │   ├── Professors/    # ProfessorType
│   │   │   │       │   ├── Enrollments/   # EnrollmentType
│   │   │   │       │   ├── Query.cs
│   │   │   │       │   └── Mutation.cs
│   │   │   │       ├── Middleware/    # GraphQLErrorFilter
│   │   │   │       └── Extensions/    # GraphQLExtensions
│   │   │   │
│   │   │   └── Driven/                # Salida (Secondary)
│   │   │       └── Persistence/       # EF Core
│   │   │           ├── Context/       # AppDbContext
│   │   │           ├── Configurations/# Fluent API configs (incl. UserConfiguration)
│   │   │           ├── Repositories/  # Student, Subject, Professor, Enrollment, User, UnitOfWork
│   │   │           ├── Services/      # JwtService, PasswordService
│   │   │           ├── DataLoaders/   # StudentById, SubjectById, ProfessorById
│   │   │           ├── Migrations/    # InitialCreate, SeedData, AddUsersTable
│   │   │           ├── Seeding/       # DataSeeder
│   │   │           ├── CompiledQueries.cs
│   │   │           └── DependencyInjection.cs
│   │   │
│   │   └── Host/                      # COMPOSITION ROOT
│   │       └── Program.cs
│   │
│   │  ═══════════════ FRONTEND (Angular 21) ═══════════════
│   │
│   └── frontend/
│       ├── src/
│       │   ├── app/
│       │   │   ├── core/              # Singleton services
│       │   │   │   ├── services/      # AuthService, StudentService, EnrollmentService, NotificationService
│       │   │   │   ├── guards/        # auth.guard.ts (authGuard, adminGuard, guestGuard)
│       │   │   │   ├── interceptors/  # ErrorInterceptor, AuthInterceptor
│       │   │   │   ├── models/        # student.model.ts
│       │   │   │   └── graphql/
│       │   │   │       ├── queries/   # students.queries.ts
│       │   │   │       ├── mutations/ # students.mutations.ts, auth.mutations.ts
│       │   │   │       └── generated/ # types.ts (Apollo codegen)
│       │   │   ├── shared/            # Componentes reutilizables
│       │   │   │   ├── components/ui/ # ConfirmDialog, EmptyState, LoadingSpinner
│       │   │   │   └── pipes/         # credits.pipe, initials.pipe
│       │   │   └── features/          # Módulos por funcionalidad
│       │   │       ├── auth/          # Autenticación
│       │   │       │   └── pages/     # Login, Register, ResetPassword
│       │   │       ├── dashboard/     # Dashboard estudiante
│       │   │       │   └── pages/     # StudentDashboard
│       │   │       ├── students/
│       │   │       │   └── pages/     # StudentList, StudentForm
│       │   │       ├── enrollment/
│       │   │       │   └── pages/     # EnrollmentPage
│       │   │       └── classmates/
│       │   │           └── pages/     # ClassmatesPage
│       │   ├── environments/          # environment.ts, environment.prod.ts
│       │   └── main.ts
│       └── e2e/                       # Playwright E2E tests
│           ├── mocks/                 # graphql.mock.ts
│           ├── student-crud.spec.ts
│           ├── enrollment.spec.ts
│           └── classmates.spec.ts
│
├── tests/                             # Backend tests
│   ├── Common/                        # Builders compartidos
│   │   └── Builders/                  # StudentBuilder, SubjectBuilder, etc.
│   ├── Domain.Tests/
│   │   ├── Entities/                  # StudentTests
│   │   ├── ValueObjects/              # EmailTests
│   │   └── Services/                  # EnrollmentDomainServiceTests
│   ├── Application.Tests/
│   │   ├── Students/                  # StudentCommandsTests, StudentQueriesTests
│   │   ├── Enrollments/               # EnrollStudent, Unenroll, GetClassmates Tests
│   │   ├── Subjects/                  # SubjectQueriesTests
│   │   ├── Professors/                # ProfessorQueriesTests
│   │   └── Validators/                # ValidatorTests
│   └── Integration.Tests/             # EnrollmentFlowTests
│
├── scripts/
│   └── dev-start.sh                   # Script desarrollo local (SQLite)
│
├── docs/
│   ├── architecture/
│   │   └── decisions/                 # ADR-001 a ADR-004
│   ├── entregables/
│   │   ├── 01-analisis/               # Requisitos, reglas, historias, riesgos
│   │   ├── 02-diseno/                 # Arquitectura, modelo dominio, DB, GraphQL, UI
│   │   └── 03-configuracion/          # Repositorio, .NET, Angular, DB, env, calidad
│   ├── qa/                            # Reportes de QA
│   │   ├── QA-REPORT-*-MANUAL-TESTS.md
│   │   └── QA-REPORT-*-REGRESSION-TESTS.md
│   ├── CODE_REVIEW_CHECKLIST.md
│   ├── DEFECTOS_QA.md                 # Defectos encontrados
│   ├── DEPLOYMENT.md
│   ├── ENTREGABLES.md
│   ├── OWASP_CHECKLIST.md
│   ├── PLAN_ACTIVIDADES.md
│   ├── RECOMMENDATIONS.md             # Recomendaciones de mejora
│   └── Prueba Técnica.md
│
├── database/
│   ├── stored-procedures/
│   └── views/
│
├── deploy/
│   ├── docker/
│   │   ├── Dockerfile.api             # Multi-stage Alpine + .NET 10
│   │   ├── Dockerfile.frontend        # Multi-stage Node + Nginx
│   │   ├── docker-compose.yml
│   │   ├── nginx.conf                 # Proxy + SPA routing
│   │   └── start.sh                   # Script de despliegue Docker
│   ├── k3s/                           # Kubernetes manifests
│   │   ├── api.yaml                   # Deployment + Service API
│   │   ├── frontend.yaml              # Deployment + Service Frontend
│   │   ├── sqlserver.yaml             # StatefulSet SQL Server
│   │   ├── ingress.yaml               # Traefik + HTTPS
│   │   ├── hpa.yaml                   # Horizontal Pod Autoscaler
│   │   ├── kustomization.yaml         # Kustomize overlay
│   │   └── deploy.sh                  # Script orquestación K3s
│   └── scripts/
│       └── deploy-k3s.sh              # Deploy remoto
│
└── .gitea/
    └── workflows/
        └── deploy.yaml                # CI/CD Pipeline
```

---

## Comandos de Desarrollo

### Backend (.NET)

```bash
# Restaurar dependencias
dotnet restore

# Build completo
dotnet build

# Ejecutar GraphQL API (puerto 5000/5001)
dotnet run --project src/backend/Host

# Tests
dotnet test                                         # Todos
dotnet test --filter "Category=Unit"                # Solo unit
dotnet test --filter "Category=Integration"         # Solo integration

# Migraciones EF Core
dotnet ef migrations add InitialCreate \
  -p src/backend/Adapters/Driven/Persistence \
  -s src/backend/Host
dotnet ef database update \
  -p src/backend/Adapters/Driven/Persistence \
  -s src/backend/Host

# Watch mode (hot reload)
dotnet watch run --project src/backend/Host

# GraphQL Playground disponible en: https://localhost:5001/graphql
```

### Frontend (Angular)

```bash
cd src/frontend

# Instalar dependencias
npm install

# Servidor desarrollo (puerto 4200)
ng serve

# Build producción
ng build --configuration production

# Tests unitarios
ng test
ng test --watch=false --code-coverage    # Con coverage

# E2E tests
npx playwright test

# Lint
ng lint

# Generar componente
ng g c features/students/components/my-component --standalone
```

### Desarrollo Local (Sin Docker)

Script que levanta backend + frontend con **SQLite** (sin necesidad de SQL Server):

```bash
# Iniciar todo
./scripts/dev-start.sh start

# Ver estado
./scripts/dev-start.sh status

# Detener
./scripts/dev-start.sh stop

# Reiniciar
./scripts/dev-start.sh restart
```

**Características:**
- Backend usa SQLite en `./data/dev.db`
- No requiere Docker ni SQL Server
- Frontend en puerto 4200
- Backend en puerto 5000
- Hot reload habilitado
- PIDs guardados para cleanup

### Docker

```bash
# Levantar todo el stack
docker-compose up -d

# Solo base de datos
docker-compose up -d sqlserver

# Rebuild
docker-compose up -d --build

# Logs
docker-compose logs -f api
```

### Docker Compose Optimizado (Producción)

Ubicación: `deploy/docker/`

```bash
cd deploy/docker
./start.sh    # Despliegue con un solo comando
```

**Recursos asignados (optimizado para 12 cores / 15GB RAM):**

| Servicio | CPU | RAM | Características |
|----------|-----|-----|-----------------|
| SQL Server | 2 cores | 2.5 GB | Volumen persistente |
| API .NET | 4 cores | 1.5 GB | Server GC, ReadyToRun, Health check |
| Frontend | 2 cores | 256 MB | Nginx + Brotli, cache agresivo |

**URLs después del despliegue:**
- Frontend: http://localhost
- API GraphQL: http://localhost:5000/graphql
- Health Check: http://localhost:5000/health

### Despliegue K3s (Producción)

**Dominio:** `https://academia.ingeniumcodex.com`

```bash
cd deploy/k3s
./deploy.sh all    # Build + transfer + deploy completo
./deploy.sh status # Ver estado
./deploy.sh clean  # Eliminar namespace
```

**URLs de producción:**
- Frontend: https://academia.ingeniumcodex.com
- API GraphQL: https://academia.ingeniumcodex.com/graphql
- Health Check: https://academia.ingeniumcodex.com/health

**Autenticación:**
- Login: https://academia.ingeniumcodex.com/login
- Registro estudiantes: https://academia.ingeniumcodex.com/register
- Reset password: https://academia.ingeniumcodex.com/reset-password
- Dashboard estudiante: https://academia.ingeniumcodex.com/dashboard

**Namespace:** `academia`

### CI/CD Pipeline (Gitea Actions)

**Ubicación:** `.gitea/workflows/deploy.yaml`

**Trigger:** Push a `main`

**Flujo automático:**
1. Checkout código
2. Sync a K3s master vía rsync
3. Build imágenes en paralelo (API + Frontend)
4. Import a K3s containerd
5. Apply Kustomize manifests
6. Rolling restart deployments
7. Health checks
8. Rollback automático si falla

**Secretos requeridos en Gitea:**
- `K3S_SSH_KEY`: Clave SSH privada
- `K3S_SUDO_PASS`: Password sudo en K3s

**Tiempo de despliegue:** ~3-5 minutos

---

## GraphQL API

**Endpoint:** `https://localhost:5001/graphql`
**Playground:** `https://localhost:5001/graphql` (Banana Cake Pop)

### Queries

```graphql
# Obtener usuario autenticado (requiere JWT)
query Me {
  me {
    id
    username
    role
    studentId
    studentName
  }
}

# Obtener todos los estudiantes
query GetStudents {
  students {
    id
    name
    email
    totalCredits
    enrollments {
      subject {
        name
        credits
        professor { name }
      }
    }
  }
}

# Obtener estudiante por ID
query GetStudentById($id: Int!) {
  student(id: $id) {
    id
    name
    email
    enrollments { subject { name } }
  }
}

# Obtener todas las materias
query GetSubjects {
  subjects {
    id
    name
    credits
    professor { id name }
  }
}

# Obtener materias disponibles para un estudiante (filtra restricciones)
query GetAvailableSubjects($studentId: Int!) {
  availableSubjects(studentId: $studentId) {
    id
    name
    credits
    professor { name }
    isAvailable          # false si ya tiene materia del mismo profesor
    unavailableReason    # "Ya tienes una materia con este profesor"
  }
}

# Ver compañeros de clase por materia
query GetClassmates($studentId: Int!) {
  classmates(studentId: $studentId) {
    subjectName
    students { name }
  }
}

# Obtener todos los profesores
query GetProfessors {
  professors {
    id
    name
    subjects { id name }
  }
}
```

### Mutations

```graphql
# ========== AUTENTICACIÓN ==========

# Login
mutation Login($input: LoginRequestInput!) {
  login(input: $input) {
    success
    token
    error
    user {
      id
      username
      role
      studentId
      studentName
    }
  }
}

# Registro (crea User + Student opcionalmente)
mutation Register($input: RegisterRequestInput!) {
  register(input: $input) {
    success
    token
    error
    recoveryCode    # Solo se muestra UNA vez
    user { id username role studentId studentName }
  }
}

# Recuperar contraseña
mutation ResetPassword($input: ResetPasswordRequestInput!) {
  resetPassword(input: $input) {
    success
    error
  }
}

# ========== ESTUDIANTES ==========

# Crear estudiante
mutation CreateStudent($input: CreateStudentInput!) {
  createStudent(input: $input) {
    student { id name email }
    errors
  }
}

# Actualizar estudiante
mutation UpdateStudent($id: Int!, $input: UpdateStudentInput!) {
  updateStudent(id: $id, input: $input) {
    student { id name email }
    errors
  }
}

# Eliminar estudiante
mutation DeleteStudent($id: Int!) {
  deleteStudent(id: $id) {
    success
    errors
  }
}

# Inscribir estudiante en materia
mutation EnrollStudent($input: EnrollStudentInput!) {
  enrollStudent(input: $input) {
    enrollment {
      id
      student { name }
      subject { name }
    }
    errors  # ["Máximo 3 materias", "Ya tienes materia con este profesor"]
  }
}

# Cancelar inscripción
mutation UnenrollStudent($enrollmentId: Int!) {
  unenrollStudent(enrollmentId: $enrollmentId) {
    success
    errors
  }
}
```

### Subscriptions (Opcional - Tiempo Real)

```graphql
# Notificación cuando un estudiante se inscribe
subscription OnStudentEnrolled {
  studentEnrolled {
    student { name }
    subject { name }
  }
}
```

---

## Autenticación

### Sistema JWT

**Algoritmo:** HMAC-SHA256
**Expiración:** Configurable (default 60 min)
**Hashing passwords:** PBKDF2-SHA256 con 100,000 iteraciones

### Flujos de Autenticación

| Flujo | Endpoint | Descripción |
|-------|----------|-------------|
| **Login** | `mutation login` | Valida credenciales, retorna JWT + UserInfo |
| **Registro** | `mutation register` | Crea User + Student (opcional), genera Recovery Code |
| **Reset Password** | `mutation resetPassword` | Valida recovery code, actualiza password |

### Entidad User

```csharp
public class User {
    public int Id;
    public string Username;           // Almacenado en minúsculas
    public string PasswordHash;        // PBKDF2-SHA256
    public string RecoveryCodeHash;    // Para reset password
    public string Role;                // "Admin" | "Student"
    public int? StudentId;             // FK a Student (nullable)
    public DateTime CreatedAt;
    public DateTime? LastLoginAt;
}
```

### Roles y Guards

| Rol | Acceso |
|-----|--------|
| **Admin** | CRUD completo de estudiantes, ver todo |
| **Student** | Dashboard propio, inscripciones propias, ver compañeros |

**Guards Angular:**
- `authGuard`: Requiere autenticación → redirige a `/login`
- `adminGuard`: Requiere role Admin → redirige a `/dashboard`
- `guestGuard`: Solo no autenticados → redirige a `/dashboard`

### Variables de Entorno JWT

```bash
JWT_SECRET_KEY          # Secret para firmar tokens (REQUERIDO, mín. 32 chars)
JWT_ISSUER              # Default: "StudentEnrollmentApi"
JWT_AUDIENCE            # Default: "StudentEnrollmentApp"
JWT_EXPIRATION_MINUTES  # Default: 60
```

### Seguridad Implementada

- **PBKDF2-SHA256** con 100,000 iteraciones (salt 16 bytes)
- **Fixed-time comparison** para recovery codes (timing attacks)
- **Rate limiting**: 30 mutations/min, 100 queries/min
- **JWT validation**: Issuer, Audience, Lifetime, SigningKey
- **Recovery code**: 12 caracteres, mostrado solo una vez
- **Serilog**: Filtra tokens y passwords de logs

---

## Validaciones de Negocio

### Ubicación en el Código

| Validación | Capa | Ruta |
|------------|------|------|
| Email válido | Domain | `Domain/ValueObjects/Email.cs` |
| Max 3 materias | Domain | `Domain/Services/EnrollmentDomainService.cs` |
| No repetir profesor | Domain | `Domain/Services/EnrollmentDomainService.cs` |
| Datos request válidos | Application | `Application/{Feature}/Commands/*Validator.cs` |
| Estudiante existe | Application | `Application/{Feature}/Commands/*Handler.cs` |
| Repos implementados | Adapters | `Adapters/Driven/Persistence/Repositories/` |

### Reglas Críticas

```csharp
// Domain/Services/EnrollmentDomainService.cs
public void ValidateEnrollment(Student student, Subject newSubject)
{
    // Regla 1: Max 3 materias
    if (student.Enrollments.Count >= 3)
        throw new MaxEnrollmentsExceededException(student.Id);

    // Regla 2: No repetir profesor
    var professorIds = student.Enrollments.Select(e => e.Subject.ProfessorId);
    if (professorIds.Contains(newSubject.ProfessorId))
        throw new SameProfessorConstraintException(student.Id, newSubject.ProfessorId);
}
```

---

## Manejo de Errores

### Códigos de Error del Backend

| Código | Mensaje Usuario | Causa |
|--------|-----------------|-------|
| `MAX_ENROLLMENTS` | Has alcanzado el límite máximo de 3 materias | Estudiante intenta inscribir 4ta materia |
| `SAME_PROFESSOR` | Ya tienes una materia con este profesor | Violación restricción profesor único |
| `STUDENT_NOT_FOUND` | El estudiante no existe en el sistema | ID inválido o eliminado |
| `SUBJECT_NOT_FOUND` | La materia seleccionada no existe | ID materia inválido |
| `DUPLICATE_ENROLLMENT` | Ya estás inscrito en esta materia | Inscripción duplicada |
| `VALIDATION_ERROR` | Los datos ingresados no son válidos | FluentValidation falló |
| `NETWORK_ERROR` | No se pudo conectar con el servidor | API no disponible |
| `INVALID_CREDENTIALS` | Usuario o contraseña incorrectos | Login fallido |
| `USERNAME_EXISTS` | El nombre de usuario ya está en uso | Registro duplicado |
| `INVALID_RECOVERY_CODE` | Código de recuperación inválido | Reset password fallido |
| `UNAUTHORIZED` | No tienes permiso para esta acción | JWT inválido o expirado |

### Frontend: Servicio de Errores

```typescript
// core/services/error-handler.service.ts
// Uso en componentes:
this.errorHandler.handle(error, 'Contexto.accion');

// Características:
// - Mensajes amigables para usuarios
// - Logging detallado en consola (solo dev)
// - Traducción automática de códigos GraphQL
```

### Backend: Filtro de Errores

```csharp
// Adapters/Driving/Api/Middleware/GraphQLErrorFilter.cs
// Transforma excepciones de dominio a errores GraphQL
// con código y mensaje estructurado
```

### Monitoreo de Conectividad

El sistema verifica la conexión con el servidor cada **5 segundos**:

| Componente | Archivo | Función |
|------------|---------|---------|
| Backend | `Host/Program.cs` | Endpoint `/health` con estado DB |
| Servicio | `core/services/connectivity.service.ts` | Polling y estado reactivo |
| Overlay | `shared/components/ui/connectivity-overlay/` | UI bloqueante |

**Comportamiento:**
- Verifica `/health` cada 5 segundos
- Requiere 2 fallos consecutivos para mostrar overlay (evita falsos positivos)
- Overlay bloquea toda interacción hasta restaurar conexión
- Muestra instrucciones para solucionar el problema

---

## Convenciones

### .NET
- **Async/Await:** Todos los métodos I/O
- **Records:** Para DTOs inmutables
- **Nullable:** Habilitado globalmente
- **Naming:** PascalCase clases/métodos, camelCase variables

### Angular
- **Standalone:** Todos los componentes
- **Signals:** Estado reactivo
- **Lazy Loading:** Por feature
- **OnPush:** Change detection strategy

### Commits
```
feat(students): add create student form
fix(enrollment): validate professor constraint
refactor(api): extract validation middleware
test(domain): add enrollment policy tests
docs(readme): update setup instructions
```

---

## Restricciones Críticas

### NUNCA
- Modificar sin leer archivo completo primero
- Archivos > 100 líneas (refactorizar)
- Hardcodear connection strings
- Lógica de negocio en Controllers
- Exponer entidades de dominio en API

### SIEMPRE
- DTOs para entrada/salida
- Tests para reglas de negocio
- Validar antes de persistir
- Async en operaciones I/O

---

## Ciclo OODA

1. **OBSERVAR:** ¿Qué se solicita? ¿Qué existe?
2. **ORIENTAR:** ¿Riesgos? ¿Restricciones?
3. **DECIDIR:** Si hay duda, investigar primero
4. **ACTUAR:** Ejecutar con precisión, validar

> **Regla de oro:** "Mide dos veces, corta una"
