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.
|
After Width: | Height: | Size: 70 KiB |
|
|
@ -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
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
|
@ -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
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
|
@ -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
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
|
@ -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
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
|
@ -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
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
|
@ -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
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
|
@ -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
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
|
@ -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
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 17 KiB |