305 lines
8.7 KiB
Markdown
305 lines
8.7 KiB
Markdown
# 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 | - | - |
|