# DI-002: Modelo de Dominio **Proyecto:** Sistema de Registro de Estudiantes **Fecha:** 2026-01-07 --- ## 1. Diagrama de Entidades ``` ┌─────────────────┐ ┌─────────────────┐ │ PROFESSOR │ │ STUDENT │ ├─────────────────┤ ├─────────────────┤ │ Id: int (PK) │ │ Id: int (PK) │ │ Name: string │ │ Name: string │ └────────┬────────┘ │ Email: Email │ │ │ RowVersion │ │ 1:2 └────────┬────────┘ ▼ │ ┌─────────────────┐ │ 0..3 │ SUBJECT │ ▼ ├─────────────────┤ ┌─────────────────┐ │ Id: int (PK) │◄──────│ ENROLLMENT │ │ Name: string │ 1:N ├─────────────────┤ │ Credits: 3 │ │ Id: int (PK) │ │ ProfessorId: FK │ │ StudentId: FK │ └─────────────────┘ │ SubjectId: FK │ │ EnrolledAt │ └─────────────────┘ ``` --- ## 2. Entidades ### Student (Aggregate Root) ```csharp public class Student { public int Id { get; private set; } public string Name { get; private set; } public Email Email { get; private set; } public byte[] RowVersion { get; private set; } private readonly List _enrollments = new(); public IReadOnlyCollection Enrollments => _enrollments; public int TotalCredits => _enrollments.Count * 3; public void Enroll(Subject subject, IEnrollmentPolicy policy) { policy.Validate(this, subject); _enrollments.Add(new Enrollment(this, subject)); } public void Unenroll(int subjectId) { var enrollment = _enrollments.FirstOrDefault(e => e.SubjectId == subjectId); if (enrollment != null) _enrollments.Remove(enrollment); } } ``` ### Subject ```csharp public class Subject { public int Id { get; private set; } public string Name { get; private set; } public int Credits { get; } = 3; // Constante: RN-002 public int ProfessorId { get; private set; } public Professor Professor { get; private set; } } ``` ### Professor ```csharp public class Professor { public int Id { get; private set; } public string Name { get; private set; } private readonly List _subjects = new(); public IReadOnlyCollection Subjects => _subjects; } ``` ### Enrollment ```csharp public class Enrollment { public int Id { get; private set; } public int StudentId { get; private set; } public int SubjectId { get; private set; } public DateTime EnrolledAt { get; private set; } public Student Student { get; private set; } public Subject Subject { get; private set; } } ``` --- ## 3. Value Objects ### Email ```csharp 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 requerido"); if (!Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$")) return Result.Failure("Formato de email inválido"); return Result.Success(new Email(email.ToLowerInvariant())); } } ``` --- ## 4. Domain Service ### EnrollmentDomainService (Implementa RN-003 y RN-005) ```csharp public class EnrollmentDomainService : IEnrollmentPolicy { public void Validate(Student student, Subject newSubject) { // RN-003: Máximo 3 materias if (student.Enrollments.Count >= 3) throw new MaxEnrollmentsExceededException(student.Id); // RN-005: No repetir profesor var professorIds = student.Enrollments .Select(e => e.Subject.ProfessorId) .ToHashSet(); if (professorIds.Contains(newSubject.ProfessorId)) throw new SameProfessorConstraintException( student.Id, newSubject.ProfessorId); } } ``` --- ## 5. Excepciones de Dominio ```csharp public class MaxEnrollmentsExceededException : DomainException { public MaxEnrollmentsExceededException(int studentId) : base($"Estudiante {studentId} ya tiene 3 materias inscritas") { } } public class SameProfessorConstraintException : DomainException { public SameProfessorConstraintException(int studentId, int professorId) : base($"Estudiante {studentId} ya tiene materia con profesor {professorId}") { } } ``` --- ## 6. Invariantes del Dominio | Invariante | Entidad | Validación | |------------|---------|------------| | Email único | Student | DB Constraint + Validator | | Email válido | Student | Value Object | | Max 3 inscripciones | Student | Domain Service | | No repetir profesor | Student | Domain Service | | 3 créditos/materia | Subject | Constante | | 2 materias/profesor | Professor | Seed Data |