# 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<Email> Create(string email)
    {
        if (string.IsNullOrWhiteSpace(email))
            return Result.Failure<Email>("EMAIL_REQUIRED");

        if (!EmailRegex.IsMatch(email))
            return Result.Failure<Email>("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 | - | - |
