# AN-002: Reglas de Negocio **Proyecto:** Sistema de Registro de Estudiantes - Inter Rapidísimo **Rol:** Analista de Sistemas **Fecha:** 2026-01-07 --- ## 1. Resumen Este documento define las reglas de negocio que gobiernan el sistema de registro de estudiantes. Estas reglas son invariantes y deben ser validadas en la capa de dominio. --- ## 2. Reglas de Negocio ### RN-001: Estructura del Catálogo de Materias | Atributo | Valor | |----------|-------| | **ID** | RN-001 | | **Nombre** | Catálogo Fijo de Materias | | **Descripción** | El sistema contiene exactamente 10 materias académicas | | **Tipo** | Restricción Estructural | | **Severidad** | Crítica | **Datos Iniciales (Seed):** | ID | Materia | Profesor Asignado | |----|---------|-------------------| | 1 | Matemáticas I | Profesor A | | 2 | Matemáticas II | Profesor A | | 3 | Física I | Profesor B | | 4 | Física II | Profesor B | | 5 | Programación I | Profesor C | | 6 | Programación II | Profesor C | | 7 | Base de Datos I | Profesor D | | 8 | Base de Datos II | Profesor D | | 9 | Redes I | Profesor E | | 10 | Redes II | Profesor E | **Validación:** `COUNT(Subjects) == 10` --- ### RN-002: Valor Uniforme de Créditos | Atributo | Valor | |----------|-------| | **ID** | RN-002 | | **Nombre** | Créditos por Materia | | **Descripción** | Cada materia equivale exactamente a 3 créditos | | **Tipo** | Restricción de Valor | | **Severidad** | Crítica | **Fórmula:** ``` Créditos_Materia = 3 (constante) Créditos_Estudiante = COUNT(Inscripciones) × 3 Créditos_Máximos = 9 (3 materias × 3 créditos) ``` **Validación:** `Subject.Credits == 3` --- ### RN-003: Límite de Inscripciones por Estudiante | Atributo | Valor | |----------|-------| | **ID** | RN-003 | | **Nombre** | Máximo 3 Materias | | **Descripción** | Un estudiante solo puede inscribirse en máximo 3 materias | | **Tipo** | Restricción de Cardinalidad | | **Severidad** | Crítica | **Escenarios:** | Materias Actuales | Acción | Resultado | |-------------------|--------|-----------| | 0-2 | Inscribir | ✓ Permitido | | 3 | Inscribir | ✗ Rechazado | | 1-3 | Cancelar | ✓ Permitido | **Validación:** `Student.Enrollments.Count <= 3` **Mensaje de Error:** "El estudiante ya tiene el máximo de 3 materias inscritas" --- ### RN-004: Asignación Profesores-Materias | Atributo | Valor | |----------|-------| | **ID** | RN-004 | | **Nombre** | Distribución de Profesores | | **Descripción** | Existen 5 profesores, cada uno dicta exactamente 2 materias | | **Tipo** | Restricción Estructural | | **Severidad** | Crítica | **Invariantes:** - `COUNT(Professors) == 5` - `∀ Professor: COUNT(Professor.Subjects) == 2` - `∀ Subject: Subject.Professor != NULL` **Validación:** ```csharp professors.All(p => p.Subjects.Count == 2) subjects.All(s => s.ProfessorId != null) ``` --- ### RN-005: Restricción de Profesor Único (CRÍTICA) | Atributo | Valor | |----------|-------| | **ID** | RN-005 | | **Nombre** | Prohibición de Mismo Profesor | | **Descripción** | Un estudiante NO puede inscribirse en dos materias dictadas por el mismo profesor | | **Tipo** | Restricción de Integridad | | **Severidad** | **CRÍTICA** | **Lógica de Validación:** ``` PARA inscribir(estudiante, nuevaMateria): profesorNuevaMateria = nuevaMateria.Profesor profesoresActuales = estudiante.Inscripciones.Select(i => i.Materia.Profesor) SI profesorNuevaMateria EN profesoresActuales: RECHAZAR "Ya tienes una materia con este profesor" SINO: PERMITIR inscripción ``` **Casos de Prueba:** | Estudiante tiene | Intenta inscribir | Resultado | |------------------|-------------------|-----------| | Matemáticas I (Prof A) | Matemáticas II (Prof A) | ✗ Rechazado | | Matemáticas I (Prof A) | Física I (Prof B) | ✓ Permitido | | Física I (Prof B), Programación I (Prof C) | Redes I (Prof E) | ✓ Permitido | | Física I (Prof B), Programación I (Prof C) | Física II (Prof B) | ✗ Rechazado | **Mensaje de Error:** "No puedes inscribir {materia} porque ya tienes una materia con {profesor}" --- ### RN-006: Unicidad de Email | Atributo | Valor | |----------|-------| | **ID** | RN-006 | | **Nombre** | Email Único por Estudiante | | **Descripción** | Cada estudiante debe tener un email único en el sistema | | **Tipo** | Restricción de Unicidad | | **Severidad** | Alta | **Validación:** `UNIQUE(Student.Email)` **Mensaje de Error:** "Ya existe un estudiante registrado con este email" --- ### RN-007: Formato de Email Válido | Atributo | Valor | |----------|-------| | **ID** | RN-007 | | **Nombre** | Validación de Email | | **Descripción** | El email debe cumplir formato estándar RFC 5322 | | **Tipo** | Restricción de Formato | | **Severidad** | Alta | **Patrón:** `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` **Mensaje de Error:** "El formato del email no es válido" --- ### RN-008: Visibilidad de Compañeros | Atributo | Valor | |----------|-------| | **ID** | RN-008 | | **Nombre** | Privacidad de Datos | | **Descripción** | Un estudiante solo puede ver el NOMBRE de sus compañeros de clase | | **Tipo** | Restricción de Privacidad | | **Severidad** | Media | **Datos Visibles:** - ✓ Nombre del compañero - ✗ Email del compañero - ✗ Otras materias del compañero **Condición:** Solo aplica para materias donde ambos estudiantes están inscritos --- ## 3. Matriz de Validación por Capa | Regla | Domain | Application | Adapter | |-------|--------|-------------|---------| | RN-001 | ✗ | ✗ | Seed Data | | RN-002 | ValueObject | ✗ | ✗ | | RN-003 | DomainService | ✗ | ✗ | | RN-004 | ✗ | ✗ | Seed Data | | RN-005 | **DomainService** | ✗ | ✗ | | RN-006 | ✗ | Validator | DB Constraint | | RN-007 | ValueObject | Validator | ✗ | | RN-008 | ✗ | Query Handler | ✗ | --- ## 4. Implementación en Código ### RN-003 y RN-005 (Domain Service) ```csharp // Domain/Services/EnrollmentDomainService.cs public class EnrollmentDomainService { public Result ValidateEnrollment(Student student, Subject newSubject) { // RN-003: Máximo 3 materias if (student.Enrollments.Count >= 3) return Result.Failure("MAX_ENROLLMENTS_EXCEEDED"); // RN-005: No repetir profesor var existingProfessorIds = student.Enrollments .Select(e => e.Subject.ProfessorId) .ToHashSet(); if (existingProfessorIds.Contains(newSubject.ProfessorId)) return Result.Failure("SAME_PROFESSOR_CONSTRAINT"); return Result.Success(); } } ``` ### RN-007 (Value Object) ```csharp // Domain/ValueObjects/Email.cs public record Email { public string Value { get; } private Email(string value) => Value = value; public static Result Create(string email) { if (string.IsNullOrWhiteSpace(email)) return Result.Failure("EMAIL_REQUIRED"); if (!EmailRegex.IsMatch(email)) return Result.Failure("EMAIL_INVALID_FORMAT"); return Result.Success(new Email(email.ToLowerInvariant())); } private static readonly Regex EmailRegex = new( @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", RegexOptions.Compiled); } ``` --- ## 5. Diagrama de Restricciones ``` ┌─────────────────────┐ │ ESTUDIANTE │ │ - Nombre │ │ - Email (único) │ └──────────┬──────────┘ │ │ máx 3 inscripciones │ (RN-003) ▼ ┌─────────────────────┐ │ INSCRIPCIÓN │ └──────────┬──────────┘ │ no puede repetir │ profesor (RN-005) ▼ ┌─────────────┐ ┌─────────────────────┐ ┌─────────────┐ │ PROFESOR │──────│ MATERIA │──────│ 3 CRÉDITOS │ │ (5 total) │ 1:2 │ (10 total) │ │ (RN-002) │ └─────────────┘ └─────────────────────┘ └─────────────┘ ``` --- ## 6. Aprobación | Rol | Nombre | Fecha | Firma | |-----|--------|-------|-------| | Analista | Sistema | 2026-01-07 | ✓ | | Product Owner | Pendiente | - | - | | Arquitecto | Pendiente | - | - |