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

305 lines
8.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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