academia/CLAUDE.md

35 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

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

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

Uso Obligatorio (SIEMPRE)

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:

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:

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"

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)

# 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

Actualización de Documentación (OBLIGATORIO)

REGLA: Después de implementar cambios significativos, SIEMPRE actualizar la documentación correspondiente.

Cuándo Actualizar

Tipo de Cambio Documentos a Actualizar
Nueva entidad/campo en dominio DI-002-modelo-dominio.md, 02-domain-model.puml
Nuevo endpoint GraphQL DI-004-esquema-graphql.md
Nueva funcionalidad AN-001-requisitos-funcionales.md, AN-003-historias-usuario.md
Cambios en arquitectura DI-001-arquitectura-backend.md, 04-components.puml
Nuevo flujo de usuario 03-sequence-*.puml, historia de usuario correspondiente
Cambios en BD DI-003-diseno-base-datos.md, 05-entity-relationship.puml
Nuevas rutas/páginas DI-005-arquitectura-frontend.md, DI-006-componentes-ui.md
Cambios en despliegue DEPLOYMENT.md, 07-deployment.puml
Cualquier cambio significativo ENTREGABLES.md, README.md

Archivos de Documentación

docs/
├── entregables/
│   ├── 01-analisis/           # Requisitos, historias, reglas
│   ├── 02-diseno/             # Arquitectura, modelo, esquemas
│   └── 03-configuracion/      # Setup, variables, calidad
├── architecture/
│   ├── decisions/             # ADRs (decisiones arquitectónicas)
│   └── diagrams/              # Diagramas PUML + SVG
├── qa/                        # Reportes de QA
├── ENTREGABLES.md             # Resumen ejecutivo
├── DEPLOYMENT.md              # Guía de despliegue
└── README.md (raíz)           # Documentación principal

Diagramas PlantUML

Ubicación: docs/architecture/diagrams/

Diagrama Archivo Actualizar cuando...
Casos de Uso 01-use-cases.puml Nuevas funcionalidades de usuario
Modelo Dominio 02-domain-model.puml Cambios en entidades/relaciones
Secuencia 03-sequence-*.puml Nuevos flujos o cambios en existentes
Componentes 04-components.puml Cambios en arquitectura
E-R 05-entity-relationship.puml Cambios en base de datos
Estados 06-state-*.puml Nuevos estados o transiciones
Despliegue 07-deployment.puml Cambios en infraestructura
C4 08-c4-context.puml Cambios en contexto del sistema

Validar y regenerar PUML después de modificar:

# Validar que compila (sin errores)
cat docs/architecture/diagrams/archivo.puml | plantuml -tpng -pipe > /dev/null && echo "OK" || echo "ERROR"

# Regenerar PNG y SVG
cd docs/architecture/diagrams
cat archivo.puml | plantuml -tpng -pipe > archivo.png
cat archivo.puml | plantuml -tsvg -pipe > archivo.svg

# Validar TODOS los diagramas
for f in docs/architecture/diagrams/*.puml; do
  echo -n "Validando $f... "
  cat "$f" | plantuml -tpng -pipe > /dev/null 2>&1 && echo "OK" || echo "ERROR"
done

Proceso de Actualización

  1. Identificar qué documentos afecta el cambio
  2. Actualizar archivos MD con la nueva información
  3. Modificar diagramas PUML si aplica
  4. Validar que los PUML compilan sin errores
  5. Regenerar PNG y SVG de diagramas modificados
  6. Verificar consistencia entre documentos

IMPORTANTE: SIEMPRE validar que los archivos PUML compilan antes de hacer commit. Un diagrama con errores de sintaxis rompe la documentación.

Ejemplo

Si se agrega un nuevo campo ActivationCode a la entidad Student:

  1. Actualizar DI-002-modelo-dominio.md (agregar campo)
  2. Actualizar 02-domain-model.puml (diagrama de clases)
  3. Actualizar DI-004-esquema-graphql.md (si se expone en API)
  4. Actualizar AN-001-requisitos-funcionales.md (si es nuevo requisito)
  5. Actualizar ENTREGABLES.md (resumen de funcionalidades)

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"