@startuml domain-model !theme plain skinparam classAttributeIconSize 0 skinparam classFontStyle bold skinparam classBackgroundColor #F8F9FA skinparam classBorderColor #495057 title Sistema de Registro de Estudiantes - Modelo de Dominio package "Domain" { class User <> { - id: int - username: string - passwordHash: string - recoveryCodeHash: string - role: string - studentId: int? - createdAt: DateTime - lastLoginAt: DateTime? -- + {static} Create(username, passwordHash, ...): User + UpdatePassword(newHash): void + UpdateLastLogin(): void + IsAdmin: bool + IsStudent: bool } class Student <> { - id: int - name: string - email: Email - activationCodeHash: string? - activationExpiresAt: DateTime? - enrollments: List -- + getTotalCredits(): int + canEnroll(): bool + hasProfessor(professorId): bool + addEnrollment(enrollment): void + removeEnrollment(enrollment): void + setActivationCode(hash, expiresIn): void + clearActivationCode(): void + isActivated: bool + isActivationExpired(): bool } class Subject <> { - id: int - name: string - credits: int {= 3} - professorId: int -- + getProfessor(): Professor } class Professor <> { - id: int - name: string - subjects: List -- + getSubjects(): List } class Enrollment <> { - id: int - studentId: int - subjectId: int - enrolledAt: DateTime -- + getStudent(): Student + getSubject(): Subject } class Email <> { - value: string -- + {static} create(value: string): Email - validate(value: string): void } class EnrollmentDomainService <> { -- + validateEnrollment(student: Student, subject: Subject): void - checkMaxEnrollments(student: Student): void - checkProfessorConstraint(student: Student, subject: Subject): void } enum UserRoles <> { Admin Student } ' Relaciones User "0..1" -- "0..1" Student : vinculado a User --> UserRoles : tiene Student "1" *-- "0..3" Enrollment : tiene Subject "1" *-- "0..*" Enrollment : inscripciones Professor "1" *-- "2" Subject : imparte Student *-- Email : email EnrollmentDomainService ..> Student : valida EnrollmentDomainService ..> Subject : valida } note bottom of User Autenticación: - PBKDF2-SHA256 (100k iter) - JWT para sesiones - Recovery code para reset end note note bottom of Student Invariantes: - Máximo 3 inscripciones - Email válido y único - No repetir profesor - Requiere activación end note note bottom of Subject Invariantes: - Créditos = 3 (fijo) - Pertenece a un profesor end note note right of Professor Cada profesor imparte exactamente 2 materias end note @enduml