academia/docs/entregables/01-analisis/reglas-negocio/AN-002-reglas-negocio.md

305 lines
8.7 KiB
Markdown
Raw Normal View History

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