academia/CLAUDE.md

31 KiB

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

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

Uso Obligatorio

Antes de cualquier operación que requiera autenticación (Gitea, K3s, SSH, etc.):

source ~/.secrets/credentials.env

Variables Disponibles

Variable Uso
GITEA_URL URL del servidor Gitea
GITEA_ADMIN_USER Usuario Gitea
GITEA_ADMIN_PASS Contraseña Gitea
K8S_MASTER_HOST Host del master K3s
K8S_SUDO_PASS Password sudo en K3s

Login Automático desde Terminal

Git (Gitea):

source ~/.secrets/credentials.env
git remote set-url gitea "https://${GITEA_ADMIN_USER}:${GITEA_ADMIN_PASS}@devops.ingeniumcodex.com/andresgarcia0313/academia.git"

API Gitea:

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

K3s (SSH):

source ~/.secrets/credentials.env
ssh $K8S_MASTER_HOST "echo $K8S_SUDO_PASS | sudo -S kubectl get pods -A"

IMPORTANTE: Siempre intentar autenticación automática usando estas variables. NO preguntar al usuario por credenciales si ya están en el archivo.


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)

# 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)

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):

# 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

# 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/

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:

Despliegue K3s (Producción)

Dominio: https://academia.ingeniumcodex.com

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

URLs de producción:

Autenticación:

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

# 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

# ========== 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)

# 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

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

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

// 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

// 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

// 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"