docs(diagrams): update architecture diagrams for activation feature

Updated PlantUML diagrams and regenerated SVG/PNG exports:

01-use-cases: Add activation and admin use cases
02-domain-model: Add activation fields to Student entity
03-sequence-enrollment: Include activation check in flow
04-components: Add activation and admin components
05-entity-relationship: Add activation columns to Student table
06-state-enrollment: Add inactive/pending states
07-deployment: Update for current infrastructure
08-c4-context: Add admin actor and activation system

All diagrams validated and exported in both SVG and PNG formats.
This commit is contained in:
Andrés Eduardo García Márquez 2026-01-09 07:43:35 -05:00
parent 8e5a401601
commit cf5ba2010d
24 changed files with 372 additions and 95 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -9,43 +9,84 @@ skinparam actorBackgroundColor #007AFF
title Sistema de Registro de Estudiantes - Diagrama de Casos de Uso title Sistema de Registro de Estudiantes - Diagrama de Casos de Uso
actor "Estudiante" as student actor "Estudiante" as student
actor "Administrador" as admin
rectangle "Sistema de Inscripción" { rectangle "Sistema de Inscripción Académica" {
usecase "Registrarse en el sistema" as UC1 ' Autenticación (ambos actores)
usecase "Iniciar sesión" as UC2 usecase "Iniciar sesión" as UC_LOGIN
usecase "Ver materias disponibles" as UC3 usecase "Recuperar contraseña" as UC_RECOVER
usecase "Inscribirse en materia" as UC4
usecase "Cancelar inscripción" as UC5
usecase "Ver mis inscripciones" as UC6
usecase "Ver compañeros de clase" as UC7
usecase "Actualizar perfil" as UC8
usecase "Validar límite de créditos\n(máx 9 créditos)" as UC4a ' Solo estudiantes
usecase "Validar restricción de profesor\n(no repetir profesor)" as UC4b usecase "Registrarse" as UC_REGISTER
usecase "Activar cuenta" as UC_ACTIVATE
usecase "Ver dashboard personal" as UC_DASHBOARD
usecase "Ver materias disponibles" as UC_SUBJECTS
usecase "Inscribirse en materia" as UC_ENROLL
usecase "Cancelar inscripción" as UC_UNENROLL
usecase "Ver mis inscripciones" as UC_MY_ENROLL
usecase "Ver compañeros de clase" as UC_CLASSMATES
' Solo administrador
usecase "Gestionar estudiantes\n(CRUD)" as UC_CRUD
usecase "Ver todos los estudiantes" as UC_LIST
usecase "Crear estudiante" as UC_CREATE
usecase "Editar estudiante" as UC_EDIT
usecase "Eliminar estudiante" as UC_DELETE
' Validaciones (includes)
usecase "Validar límite de créditos\n(máx 9 créditos)" as UC_VAL_CREDITS
usecase "Validar restricción de profesor\n(no repetir profesor)" as UC_VAL_PROF
} }
student --> UC1 ' Relaciones Estudiante
student --> UC2 student --> UC_REGISTER
student --> UC3 student --> UC_LOGIN
student --> UC4 student --> UC_ACTIVATE
student --> UC5 student --> UC_RECOVER
student --> UC6 student --> UC_DASHBOARD
student --> UC7 student --> UC_SUBJECTS
student --> UC8 student --> UC_ENROLL
student --> UC_UNENROLL
student --> UC_MY_ENROLL
student --> UC_CLASSMATES
UC4 ..> UC4a : <<include>> ' Relaciones Admin
UC4 ..> UC4b : <<include>> admin --> UC_LOGIN
admin --> UC_RECOVER
admin --> UC_CRUD
admin --> UC_LIST
note right of UC4 ' Extensiones CRUD
UC_CRUD ..> UC_CREATE : <<include>>
UC_CRUD ..> UC_EDIT : <<include>>
UC_CRUD ..> UC_DELETE : <<include>>
' Validaciones inscripción
UC_ENROLL ..> UC_VAL_CREDITS : <<include>>
UC_ENROLL ..> UC_VAL_PROF : <<include>>
note right of UC_ENROLL
Reglas de negocio: Reglas de negocio:
- Máximo 3 materias (9 créditos) - Máximo 3 materias (9 créditos)
- No puede tener 2 materias - No puede tener 2 materias
del mismo profesor del mismo profesor
- Requiere cuenta activada
end note end note
note right of UC7 note right of UC_CLASSMATES
Solo muestra nombres Solo muestra nombres
de compañeros por materia de compañeros por materia
end note end note
note right of UC_ACTIVATE
El estudiante recibe
código de activación
al registrarse
end note
note bottom of admin
Acceso completo al
CRUD de estudiantes
end note
@enduml @enduml

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@ -9,16 +9,40 @@ title Sistema de Registro de Estudiantes - Modelo de Dominio
package "Domain" { package "Domain" {
class User <<Entity>> {
- 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 <<Entity>> { class Student <<Entity>> {
- id: int - id: int
- name: string - name: string
- email: Email - email: Email
- activationCodeHash: string?
- activationExpiresAt: DateTime?
- enrollments: List<Enrollment> - enrollments: List<Enrollment>
-- --
+ getTotalCredits(): int + getTotalCredits(): int
+ canEnrollIn(subject: Subject): bool + canEnroll(): bool
+ enroll(subject: Subject): Enrollment + hasProfessor(professorId): bool
+ unenroll(enrollmentId: int): void + addEnrollment(enrollment): void
+ removeEnrollment(enrollment): void
+ setActivationCode(hash, expiresIn): void
+ clearActivationCode(): void
+ isActivated: bool
+ isActivationExpired(): bool
} }
class Subject <<Entity>> { class Subject <<Entity>> {
@ -62,7 +86,14 @@ package "Domain" {
- checkProfessorConstraint(student: Student, subject: Subject): void - checkProfessorConstraint(student: Student, subject: Subject): void
} }
enum UserRoles <<Enumeration>> {
Admin
Student
}
' Relaciones ' Relaciones
User "0..1" -- "0..1" Student : vinculado a
User --> UserRoles : tiene
Student "1" *-- "0..3" Enrollment : tiene Student "1" *-- "0..3" Enrollment : tiene
Subject "1" *-- "0..*" Enrollment : inscripciones Subject "1" *-- "0..*" Enrollment : inscripciones
Professor "1" *-- "2" Subject : imparte Professor "1" *-- "2" Subject : imparte
@ -72,11 +103,19 @@ package "Domain" {
EnrollmentDomainService ..> Subject : valida EnrollmentDomainService ..> Subject : valida
} }
note bottom of User
<b>Autenticación:</b>
- PBKDF2-SHA256 (100k iter)
- JWT para sesiones
- Recovery code para reset
end note
note bottom of Student note bottom of Student
<b>Invariantes:</b> <b>Invariantes:</b>
- Máximo 3 inscripciones - Máximo 3 inscripciones
- Email válido y único - Email válido y único
- No repetir profesor - No repetir profesor
- Requiere activación
end note end note
note bottom of Subject note bottom of Subject

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@ -5,11 +5,12 @@ skinparam responseMessageBelowArrow true
skinparam sequenceParticipantBackgroundColor #F8F9FA skinparam sequenceParticipantBackgroundColor #F8F9FA
skinparam sequenceParticipantBorderColor #495057 skinparam sequenceParticipantBorderColor #495057
title Secuencia: Inscripción de Estudiante en Materia title Secuencia: Inscripción de Estudiante en Materia (con JWT)
actor "Estudiante" as user actor "Estudiante" as user
participant "Frontend\n(Angular)" as frontend participant "Frontend\n(Angular)" as frontend
participant "API GraphQL\n(HotChocolate)" as api participant "API GraphQL\n(HotChocolate)" as api
participant "JWT Middleware" as jwt
participant "EnrollStudentHandler" as handler participant "EnrollStudentHandler" as handler
participant "EnrollmentDomainService" as domainService participant "EnrollmentDomainService" as domainService
participant "StudentRepository" as studentRepo participant "StudentRepository" as studentRepo
@ -17,14 +18,27 @@ participant "SubjectRepository" as subjectRepo
participant "EnrollmentRepository" as enrollRepo participant "EnrollmentRepository" as enrollRepo
database "SQL Server" as db 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 == == Solicitud de Inscripción ==
user -> frontend : Selecciona materia\ny hace clic en "Inscribir" user -> frontend : Selecciona materia\ny hace clic en "Inscribir"
activate frontend activate frontend
frontend -> api : mutation enrollStudent(\n studentId, subjectId) frontend -> api : mutation enrollStudent(\n studentId, subjectId)\n[Authorization: Bearer <JWT>]
activate api activate api
api -> jwt : Validate JWT
activate jwt
jwt -> jwt : Verify signature\n& expiration
jwt --> api : ClaimsPrincipal
deactivate jwt
api -> handler : Handle(EnrollStudentCommand) api -> handler : Handle(EnrollStudentCommand)
activate handler activate handler
@ -37,6 +51,12 @@ db --> studentRepo : Student data
studentRepo --> handler : Student studentRepo --> handler : Student
deactivate studentRepo 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) handler -> subjectRepo : GetByIdAsync(subjectId)
activate subjectRepo activate subjectRepo
subjectRepo -> db : SELECT Subject subjectRepo -> db : SELECT Subject

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View File

@ -11,19 +11,43 @@ title Sistema de Registro de Estudiantes - Arquitectura de Componentes
package "Frontend (Angular 21)" as frontend { package "Frontend (Angular 21)" as frontend {
[App Component] as app [App Component] as app
package "Features" {
package "Auth" {
[Login Page] as loginPage
[Register Page] as registerPage
[Reset Password] as resetPage
[Activate Account] as activatePage
}
package "Dashboard" {
[Student Dashboard] as studentDash
[Admin Dashboard] as adminDash
}
package "Students" {
[Student List] as studentList [Student List] as studentList
[Student Form] as studentForm [Student Form] as studentForm
}
package "Enrollment" {
[Enrollment Page] as enrollPage [Enrollment Page] as enrollPage
[Classmates Page] as classmates [Classmates Page] as classmates
}
}
package "Core" { package "Core" {
[Apollo Client] as apollo [Apollo Client] as apollo
[Auth Service] as authSvc
[Student Service] as studentSvc [Student Service] as studentSvc
[Enrollment Service] as enrollSvc [Enrollment Service] as enrollSvc
[Connectivity Service] as connSvc [Connectivity Service] as connSvc
[Error Handler] as errorHandler [Error Handler] as errorHandler
} }
package "Guards" {
[Auth Guard] as authGuard
[Admin Guard] as adminGuard
[Guest Guard] as guestGuard
}
package "Shared" { package "Shared" {
[Connectivity Overlay] as overlay [Connectivity Overlay] as overlay
[Loading Spinner] as spinner [Loading Spinner] as spinner
@ -43,26 +67,42 @@ package "Backend (.NET 10)" as backend {
[GraphQL API\n(HotChocolate)] as graphql [GraphQL API\n(HotChocolate)] as graphql
[Query] as query [Query] as query
[Mutation] as mutation [Mutation] as mutation
[Types] as types [Auth Types] as authTypes
[Student Types] as studentTypes
} }
package "Driven (Secondary)" { package "Driven (Secondary)" {
[Repositories] as repos [Repositories] as repos
[DataLoaders] as loaders [DataLoaders] as loaders
[DbContext] as dbContext [DbContext] as dbContext
[JWT Service] as jwtSvc
[Password Service] as passSvc
} }
} }
package "Application" as application { package "Application" as application {
[Commands] as commands package "Auth" {
[Queries] as queries [Login Command] as loginCmd
[Handlers] as handlers [Register Command] as registerCmd
[Reset Password] as resetCmd
}
package "Students" {
[Student Commands] as studentCmds
[Student Queries] as studentQs
}
package "Enrollments" {
[Enroll Commands] as enrollCmds
[Classmates Query] as classmatesQ
}
[Validators] as validators [Validators] as validators
[DTOs] as dtos [DTOs] as dtos
} }
package "Domain" as domain { package "Domain" as domain {
[Entities] as entities [User Entity] as userEntity
[Student Entity] as studentEntity
[Subject Entity] as subjectEntity
[Enrollment Entity] as enrollEntity
[Value Objects] as valueObjects [Value Objects] as valueObjects
[Domain Services] as domainSvc [Domain Services] as domainSvc
[Ports (Interfaces)] as ports [Ports (Interfaces)] as ports
@ -70,46 +110,67 @@ package "Backend (.NET 10)" as backend {
} }
database "SQL Server 2022" as sqlserver { database "SQL Server 2022" as sqlserver {
[Students] [Users Table] as tblUsers
[Subjects] [Students Table] as tblStudents
[Professors] [Subjects Table] as tblSubjects
[Enrollments] [Professors Table] as tblProf
[Enrollments Table] as tblEnroll
} }
cloud "Browser" as browser cloud "Browser" as browser
' Conexiones Frontend ' Conexiones Frontend
browser --> app browser --> app
app --> loginPage
app --> registerPage
app --> studentDash
app --> adminDash
app --> studentList app --> studentList
app --> studentForm
app --> enrollPage app --> enrollPage
app --> classmates
app --> overlay app --> overlay
loginPage --> authSvc
registerPage --> authSvc
resetPage --> authSvc
activatePage --> authSvc
studentDash --> studentSvc
adminDash --> studentSvc
studentList --> studentSvc studentList --> studentSvc
studentForm --> studentSvc
enrollPage --> enrollSvc enrollPage --> enrollSvc
classmates --> enrollSvc classmates --> enrollSvc
overlay --> connSvc overlay --> connSvc
authSvc --> apollo
studentSvc --> apollo studentSvc --> apollo
enrollSvc --> apollo enrollSvc --> apollo
connSvc ..> errorHandler
' Guards
authGuard ..> authSvc
adminGuard ..> authSvc
guestGuard ..> authSvc
' Conexiones Backend ' Conexiones Backend
apollo --> graphql : HTTP/GraphQL apollo --> graphql : HTTP/GraphQL\n+ JWT Header
graphql --> query graphql --> query
graphql --> mutation graphql --> mutation
query --> handlers graphql --> authTypes
mutation --> handlers
handlers --> validators
handlers --> commands
handlers --> queries
commands --> domainSvc mutation --> loginCmd
queries --> repos mutation --> registerCmd
domainSvc --> entities mutation --> resetCmd
mutation --> studentCmds
mutation --> enrollCmds
query --> studentQs
query --> classmatesQ
loginCmd --> jwtSvc
loginCmd --> passSvc
registerCmd --> passSvc
studentCmds --> domainSvc
enrollCmds --> domainSvc
domainSvc --> studentEntity
domainSvc --> valueObjects domainSvc --> valueObjects
repos --> dbContext repos --> dbContext
@ -118,6 +179,7 @@ dbContext --> sqlserver
' Implementación de puertos ' Implementación de puertos
repos ..|> ports : implements repos ..|> ports : implements
jwtSvc ..|> ports : implements
note right of domain note right of domain
<b>Regla de Dependencia:</b> <b>Regla de Dependencia:</b>
@ -129,6 +191,12 @@ note bottom of graphql
Endpoints: Endpoints:
- /graphql - /graphql
- /health - /health
Auth: JWT Bearer
end note
note right of jwtSvc
HMAC-SHA256
Configurable expiration
end note end note
@enduml @enduml

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -6,11 +6,25 @@ skinparam classBorderColor #495057
title Sistema de Registro de Estudiantes - Diagrama Entidad-Relación title Sistema de Registro de Estudiantes - Diagrama Entidad-Relación
entity "Users" as users {
* **Id** : int <<PK>>
--
* Username : nvarchar(50) <<unique>>
* PasswordHash : nvarchar(255)
* RecoveryCodeHash : nvarchar(255)
* Role : nvarchar(20)
StudentId : int <<FK, nullable>>
* CreatedAt : datetime2
LastLoginAt : datetime2
}
entity "Students" as students { entity "Students" as students {
* **Id** : int <<PK>> * **Id** : int <<PK>>
-- --
* Name : nvarchar(100) * Name : nvarchar(100)
* Email : nvarchar(255) <<unique>> * Email : nvarchar(255) <<unique>>
ActivationCodeHash : nvarchar(255)
ActivationExpiresAt : datetime2
* CreatedAt : datetime2 * CreatedAt : datetime2
UpdatedAt : datetime2 UpdatedAt : datetime2
} }
@ -40,14 +54,23 @@ entity "Enrollments" as enrollments {
} }
' Relaciones ' Relaciones
users ||--o| students : "vinculado a"
students ||--o{ enrollments : "tiene" students ||--o{ enrollments : "tiene"
subjects ||--o{ enrollments : "inscripciones" subjects ||--o{ enrollments : "inscripciones"
professors ||--|| subjects : "imparte 2" professors ||--|| subjects : "imparte 2"
note right of users
<b>Autenticación:</b>
- Password: PBKDF2-SHA256
- Roles: Admin, Student
- Recovery code para reset
end note
note right of students note right of students
<b>Restricciones:</b> <b>Restricciones:</b>
- Email único - Email único
- Máximo 3 enrollments - Máximo 3 enrollments
- Activación requerida
end note end note
note right of subjects note right of subjects

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -3,10 +3,19 @@
skinparam stateBackgroundColor #F8F9FA skinparam stateBackgroundColor #F8F9FA
skinparam stateBorderColor #495057 skinparam stateBorderColor #495057
title Estado de Inscripción del Estudiante title Estados del Estudiante y sus Inscripciones
[*] --> SinMaterias : Registro inicial [*] --> Registrado : Registro inicial
state "Cuenta" as cuenta {
state "Registrado\n(Pendiente Activación)" as Registrado
state "Activo" as Activo
Registrado --> Activo : activar(código)
Registrado --> Registrado : código expirado\n[regenerar código]
}
state "Inscripciones" as inscripciones {
state "Sin Materias" as SinMaterias { state "Sin Materias" as SinMaterias {
state "0 créditos" as cred0 state "0 créditos" as cred0
} }
@ -25,6 +34,23 @@ Parcial --> Parcial : inscribir(materia)\n[créditos < 9]
Parcial --> Completa : inscribir(materia)\n[créditos = 6] Parcial --> Completa : inscribir(materia)\n[créditos = 6]
Completa --> Parcial : cancelar(inscripción) Completa --> Parcial : cancelar(inscripción)
Parcial --> SinMaterias : cancelar(inscripción)\n[única materia] Parcial --> SinMaterias : cancelar(inscripción)\n[única materia]
}
Activo --> SinMaterias : cuenta activa
state validacion <<choice>>
SinMaterias --> validacion : intenta inscribir
Parcial --> validacion : intenta inscribir
validacion --> Parcial : [válido y cuenta activa]
validacion --> [*] : [inválido]\nmuestra error
note right of Registrado
El estudiante recibe
código de activación
por email (24h validez)
end note
note right of Completa note right of Completa
No puede inscribir No puede inscribir
@ -33,17 +59,16 @@ end note
note left of Parcial note left of Parcial
<b>Validaciones en cada inscripción:</b> <b>Validaciones en cada inscripción:</b>
- Cuenta activa
- Límite de créditos - Límite de créditos
- No repetir profesor - No repetir profesor
- Materia no duplicada - Materia no duplicada
end note end note
state validacion <<choice>> note bottom of validacion
Si la cuenta no está
SinMaterias --> validacion : intenta inscribir activa, redirige a
Parcial --> validacion : intenta inscribir página de activación
end note
validacion --> Parcial : [válido]
validacion --> [*] : [inválido]\nmuestra error
@enduml @enduml

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -12,35 +12,58 @@ node "Cliente" as client {
} }
} }
node "Docker Host" as docker { node "K3s Cluster (Namespace: academia)" as k3s {
node "student-frontend" as frontendContainer <<container>> { node "frontend-deployment" as frontendPod <<Pod>> {
component "Nginx" as nginx { component "Nginx" as nginx {
[Static Files] [Static Files]
[Reverse Proxy] [Reverse Proxy]
} }
} }
node "student-api" as apiContainer <<container>> { node "api-deployment" as apiPod <<Pod>> {
component "ASP.NET Core" as aspnet { component "ASP.NET Core" as aspnet {
[Kestrel Server] [Kestrel Server]
[GraphQL Endpoint] [GraphQL Endpoint]
[JWT Auth]
[Health Check] [Health Check]
} }
} }
node "student-db" as dbContainer <<container>> { node "sqlserver-statefulset" as dbPod <<StatefulSet>> {
database "SQL Server 2022" as sqlserver { database "SQL Server 2022" as sqlserver {
[StudentEnrollment DB] [StudentEnrollment DB]
} }
} }
node "Traefik Ingress" as ingress <<Ingress>> {
[TLS Termination]
[Routing Rules]
} }
component "NetworkPolicy" as netpol <<Security>> {
[default-deny-ingress]
[allow-frontend-ingress]
[allow-api-ingress]
[allow-sqlserver-from-api]
}
}
cloud "Internet" as internet
' Conexiones ' Conexiones
browser --> nginx : HTTP :80 browser --> internet : HTTPS
internet --> ingress : HTTPS :443
ingress --> nginx : HTTP :80
nginx --> aspnet : HTTP :5000\n/graphql nginx --> aspnet : HTTP :5000\n/graphql
aspnet --> sqlserver : TCP :1433 aspnet --> sqlserver : TCP :1433
note right of ingress
<b>Dominio:</b>
academia.ingeniumcodex.com
<b>TLS:</b> Let's Encrypt
end note
note right of nginx note right of nginx
<b>Nginx Config:</b> <b>Nginx Config:</b>
- Gzip/Brotli compression - Gzip/Brotli compression
@ -55,6 +78,7 @@ note right of aspnet
- ReadyToRun - ReadyToRun
- Connection pooling - Connection pooling
- Rate limiting - Rate limiting
- JWT validation
end note end note
note right of sqlserver note right of sqlserver
@ -64,4 +88,16 @@ note right of sqlserver
- Persistent volume - Persistent volume
end note end note
note bottom of k3s
<b>CI/CD:</b> Gitea Actions
<b>Namespace:</b> academia
<b>Seguridad:</b> NetworkPolicy
end note
note right of netpol
<b>Flujo permitido:</b>
Ingress → Frontend → API → SQL
(Todo otro tráfico bloqueado)
end note
@enduml @enduml

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -10,28 +10,44 @@ skinparam rectangle {
} }
actor "Estudiante" as student <<Persona>> actor "Estudiante" as student <<Persona>>
actor "Administrador" as admin <<Persona>>
rectangle "Sistema de Registro\nde Estudiantes" as system <<Software System>> #lightblue { rectangle "Sistema de Inscripción\nAcadémica" as system <<Software System>> #lightblue {
} }
rectangle "Base de Datos\nSQL Server" as database <<External System>> #lightgray { rectangle "Base de Datos\nSQL Server" as database <<External System>> #lightgray {
} }
student --> system : Usa para registrarse\ne inscribirse en materias rectangle "Servidor SMTP\n(Email)" as smtp <<External System>> #lightgray {
system --> database : Lee y escribe\ndatos de inscripciones }
student --> system : Se registra, activa cuenta,\nse inscribe en materias,\nve compañeros de clase
admin --> system : Gestiona estudiantes\n(CRUD completo)
system --> database : Lee y escribe\ndatos de usuarios,\nestudiantes e inscripciones
system --> smtp : Envía códigos\nde activación
note right of student note right of student
<b>Estudiante</b> <b>Estudiante</b>
Usuario del sistema que: Usuario del sistema que:
- Se registra en el sistema - Se registra y activa cuenta
- Se inscribe en materias (máx 3) - Se inscribe en materias (máx 3)
- Ve sus compañeros de clase - Ve sus compañeros de clase
- Consulta inscripciones - Consulta sus inscripciones
- Accede a su dashboard personal
end note
note left of admin
<b>Administrador</b>
Usuario privilegiado que:
- Gestiona todos los estudiantes
- Crea, edita, elimina registros
- Visualiza todo el sistema
end note end note
note right of system note right of system
<b>Sistema de Registro</b> <b>Sistema de Inscripción Académica</b>
Aplicación web que permite: Aplicación web que permite:
- Autenticación (JWT + PBKDF2)
- CRUD de estudiantes - CRUD de estudiantes
- Inscripción en materias - Inscripción en materias
- Validación de reglas de negocio - Validación de reglas de negocio
@ -40,15 +56,24 @@ note right of system
<b>Stack:</b> <b>Stack:</b>
Frontend: Angular 21 Frontend: Angular 21
Backend: .NET 10 + GraphQL Backend: .NET 10 + GraphQL
Auth: JWT + Roles (Admin/Student)
end note end note
note right of database note right of database
<b>SQL Server 2022</b> <b>SQL Server 2022</b>
Almacena: Almacena:
- Usuarios (auth)
- Estudiantes - Estudiantes
- Profesores - Profesores
- Materias - Materias
- Inscripciones - Inscripciones
end note end note
note right of smtp
<b>Servicio de Email</b>
Para:
- Códigos de activación
- Notificaciones
end note
@enduml @enduml

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 17 KiB