@startuml sequence-enrollment !theme plain skinparam sequenceMessageAlign center skinparam responseMessageBelowArrow true skinparam sequenceParticipantBackgroundColor #F8F9FA skinparam sequenceParticipantBorderColor #495057 title Secuencia: Inscripción de Estudiante en Materia (con JWT) actor "Estudiante" as user participant "Frontend\n(Angular)" as frontend participant "API GraphQL\n(HotChocolate)" as api participant "JWT Middleware" as jwt participant "EnrollStudentHandler" as handler participant "EnrollmentDomainService" as domainService participant "StudentRepository" as studentRepo participant "SubjectRepository" as subjectRepo participant "EnrollmentRepository" as enrollRepo database "SQL Server" as db == Autenticación (previo) == note over user, frontend El estudiante ya inició sesión y tiene un JWT válido almacenado end note == Solicitud de Inscripción == user -> frontend : Selecciona materia\ny hace clic en "Inscribir" activate frontend frontend -> api : mutation enrollStudent(\n studentId, subjectId)\n[Authorization: Bearer ] activate api api -> jwt : Validate JWT activate jwt jwt -> jwt : Verify signature\n& expiration jwt --> api : ClaimsPrincipal deactivate jwt api -> handler : Handle(EnrollStudentCommand) activate handler == Obtención de Datos == handler -> studentRepo : GetByIdWithEnrollmentsAsync(studentId) activate studentRepo studentRepo -> db : SELECT Student + Enrollments db --> studentRepo : Student data studentRepo --> handler : Student deactivate studentRepo alt Cuenta no activada handler --> api : Error: "Cuenta no activada" api --> frontend : { errors: [...] } frontend --> user : Muestra mensaje:\n"Activa tu cuenta primero" end handler -> subjectRepo : GetByIdAsync(subjectId) activate subjectRepo subjectRepo -> db : SELECT Subject db --> subjectRepo : Subject data subjectRepo --> handler : Subject deactivate subjectRepo == Validación de Reglas de Negocio == handler -> domainService : ValidateEnrollment(student, subject) activate domainService domainService -> domainService : CheckMaxEnrollments()\n[máx 3 materias] alt Estudiante tiene 3 materias domainService --> handler : throw MaxEnrollmentsExceededException handler --> api : Error: "Límite de materias alcanzado" api --> frontend : { errors: [...] } frontend --> user : Muestra mensaje de error end domainService -> domainService : CheckProfessorConstraint()\n[no repetir profesor] alt Ya tiene materia con el profesor domainService --> handler : throw SameProfessorConstraintException handler --> api : Error: "Ya tienes materia con este profesor" api --> frontend : { errors: [...] } frontend --> user : Muestra mensaje de error end domainService --> handler : Validación OK deactivate domainService == Persistencia == handler -> enrollRepo : AddAsync(enrollment) activate enrollRepo enrollRepo -> db : INSERT Enrollment db --> enrollRepo : OK enrollRepo --> handler : Enrollment deactivate enrollRepo handler --> api : EnrollmentPayload deactivate handler api --> frontend : { enrollment: {...} } deactivate api frontend --> user : Muestra confirmación:\n"Inscrito en [materia]" deactivate frontend @enduml