- Nombre del Equipo: Dexo Corp
- Integrantes:
- Mogollon Caceres Sergio Daniel
- Davis Coropuna Leon Felipe
- Apaza Apaza Nelzon Jorge
- Lupo Condori Avelino
- Maldonado Casilla Braulio Nayap
- Parizaca Mozo Paul Antony
- Huaman Coaquira Luciana Julissa
- Organización: Universidad Nacional de San Agustín (UNSA)
El propósito de este proyecto es desarrollar un Sistema de Gestión de Matrículas (SGM) que optimice los procesos de inscripción y gestión de matrículas de los estudiantes de la Universidad Nacional de San Agustín (UNSA). Este sistema tiene como objetivo ser escalable, seguro y fácil de usar, basado en una arquitectura moderna de microservicios.
Requisitos Funcionales:
- RF1: Gestionar Autenticación
- RF2: Gestionar Usuarios
- RF3: Gestionar Cursos
- RF4: Gestionar Horarios
- RF5: Realizar Matrícula
- RF6: Generar Reportes de Matrícula
- RF7: Gestionar Escuelas
- RF8: Gestionar Notificaciones
- RF9: Gestionar Pagos
- RF10: Gestionar Cupos
Requisitos No Funcionales:
- RNF1: Usabilidad
- RNF2: Rendimiento
- RNF3: Seguridad
- RNF4: Escalabilidad
- RNF5: Disponibilidad
- RNF6: Mantenibilidad
- RNF7: Compatibilidad
Gestionar Autenticación
- Feature 1.1: Autenticación de Usuarios
- Feature 1.2: Recuperación de Contraseña
Gestionar Usuarios
- Feature 2.1: Creación de Usuarios
- Feature 2.2: Actualización de Usuarios
- Feature 2.3: Eliminación de Usuarios
Gestionar Cursos
- Feature 3.1: Creación de Cursos
- Feature 3.2: Actualización de Cursos
- Feature 3.3: Eliminación de Cursos
Gestionar Horarios
- Feature 4.1: Creación de Horarios
- Feature 4.2: Actualización de Horarios
- Feature 4.3: Eliminación de Horarios
Realizar Matrícula
- Feature 5.1: Inscripción de Estudiantes
- Feature 5.2: Actualización de Matrícula
- Feature 5.3: Cancelación de Matrícula
Generar Reportes de Matrícula
- Feature 6.1: Reporte de Inscripciones
- Feature 6.2: Reporte de Pagos
Gestionar Escuelas
- Feature 7.1: Creación de Escuelas
- Feature 7.2: Actualización de Escuelas
- Feature 7.3: Eliminación de Escuelas
Gestionar Notificaciones
- Feature 8.1: Envío de Notificaciones
- Feature 8.2: Gestión de Plantillas
Gestionar Pagos
- Feature 9.1: Procesamiento de Pagos
- Feature 9.2: Verificación de Pagos
Gestionar Cupos
- Feature 10.1: Asignación de Cupos
- Feature 10.2: Actualización de Cupos
- Feature 10.3: Cancelación de Cupos
Descripción general de la arquitectura de microservicios.
- Microservicio de Autenticación
- Microservicio de Usuarios
- Microservicio de Cursos
- Microservicio de Matrícula
- Microservicio de Escuelas
- Microservicio de Notificaciones
- Microservicio de Pagos
- Backend:
.NET
: Framework de desarrollo para la implementación de los microservicios backend.C#
: Lenguaje de programación para el desarrollo de los microservicios backend.Entity Framework Core
: ORM para el mapeo de objetos a relaciones.NUnit
: Framework de pruebas unitarias para las pruebas del backend.
- Frontend:
React
: Biblioteca de JavaScript para la creación de interfaces de usuario declarativas.Redux
: Librería para la gestión del estado de la aplicación.TypeScript
: Lenguaje de programación superconjunto de JavaScript con tipado estático.SASS
: Preprocesador de CSS para la creación de estilos CSS más organizados y mantenibles.
- Mensajería:
RabbitMQ
: Sistema de mensajería asíncrona para la comunicación entre microservicios.
- Base de Datos:
Mysql
: Sistema de gestión de bases de datos relacionales.Mongodb
: Base de datos NoSQL de código abierto.
El código ha sido desarrollado siguiendo las mejores prácticas de clean code para asegurar su mantenibilidad, legibilidad y escalabilidad. Algunos de los principios aplicados incluyen:
- Nombres significativos: Uso de nombres claros y descriptivos para variables, funciones y clases.
- Funciones pequeñas: Funciones que realizan una única tarea específica.
- Comentarios útiles: Comentarios que explican el "por qué" detrás de una decisión de código.
- Manejo de errores: Manejo adecuado de excepciones y errores para prevenir fallos inesperados.
El diseño del sistema sigue los principios SOLID para garantizar una arquitectura robusta y flexible:
-
S - Principio de Responsabilidad Única (Single Responsibility Principle): Cada clase debe tener una única responsabilidad. Por ejemplo, una clase
UserService
se encarga únicamente de la lógica relacionada con los usuarios.public class UserService { private readonly IUserRepository _userRepository; public UserService(IUserRepository userRepository) { _userRepository = userRepository; } public void AddUser(User user) { // ... } }
-
O - Principio de Abierto/Cerrado (Open/Closed Principle): Las clases deben estar abiertas para extensión, pero cerradas para modificación. Utilizamos la herencia y la interfaz para extender el comportamiento sin modificar el código existente.
public interface IPaymentProcessor { void ProcessPayment(Payment payment); } public class CreditCardPaymentProcessor : IPaymentProcessor { public void ProcessPayment(Payment payment) { // ... } }
-
L - Principio de Sustitución de Liskov (Liskov Substitution Principle): Las clases derivadas deben ser sustituibles por sus clases base. Las clases hijas deben implementar completamente la funcionalidad esperada por la clase padre.
namespace users_microservice.Domain.ValueObjects { public class StudentInfo { // Details related to student information } public class AcademicPerformance { // Details related to academic performance } public class CourseModel { // Details related to course model } } namespace users_microservice.Domain.Entities { public class UserModel { public string? Username { get; set; } public string? Email { get; set; } public virtual void DisplayInfo() { Console.WriteLine($"Username: {Username}, Email: {Email}"); } } public class StudentModel : UserModel { public StudentInfo? StudentData { get; set; } public int? Credit { get; set; } public AcademicPerformance? AcademicPerformance { get; set; } public List<CourseModel>? StudentCourses { get; set; } public StudentModel() { } public override void DisplayInfo() { base.DisplayInfo(); Console.WriteLine("Student Data and additional information."); } } public class AdminModel : UserModel { public string? PhoneNumber { get; set; } public AdminModel() { } public override void DisplayInfo() { base.DisplayInfo(); Console.WriteLine($"Phone Number: {PhoneNumber}"); } } } namespace users_microservice.Application.Services { public interface IUserService { void AddUser(UserModel user); UserModel GetUser(string username); List<UserModel> GetAllUsers(); } public class UserService : IUserService { private readonly List<UserModel> _users = new List<UserModel>(); public void AddUser(UserModel user) { _users.Add(user); } public UserModel GetUser(string username) { return _users.FirstOrDefault(user => user.Username == username); } public List<UserModel> GetAllUsers() { return _users; } } }
- Ejemplo de uso:
IUserService userService = new UserService(); // Crear instancias de StudentModel y AdminModel UserModel student = new StudentModel { Username = "john_doe", Email = "john@example.com", StudentData = new StudentInfo(), Credit = 30, AcademicPerformance = new AcademicPerformance(), StudentCourses = new List<CourseModel>() }; UserModel admin = new AdminModel { Username = "admin_user", Email = "admin@example.com", PhoneNumber = "123-456-7890" }; // Agregar usuarios al servicio userService.AddUser(student); userService.AddUser(admin);
-
I - Principio de Segregación de Interfaces (Interface Segregation Principle): Una clase no debería estar forzada a implementar interfaces que no usa. Dividimos las interfaces grandes en otras más pequeñas y específicas.
// Interfaces Segregadas public interface IStudentService { void AddStudent(StudentModel student); StudentModel GetStudent(string username); List<StudentModel> GetAllStudents(); } public interface IAdminService { void AddAdmin(AdminModel admin); AdminModel GetAdmin(string username); List<AdminModel> GetAllAdmins(); } // StudentService public class StudentService : IStudentService { private readonly List<StudentModel> _students = new List<StudentModel>(); public void AddStudent(StudentModel student) { _students.Add(student); } public StudentModel GetStudent(string username) { return _students.FirstOrDefault(student => student.Username == username); } public List<StudentModel> GetAllStudents() { return _students; } } // AdminService public class AdminService : IAdminService { private readonly List<AdminModel> _admins = new List<AdminModel>(); public void AddAdmin(AdminModel admin) { _admins.Add(admin); } public AdminModel GetAdmin(string username) { return _admins.FirstOrDefault(admin => admin.Username == username); } public List<AdminModel> GetAllAdmins() { return _admins; } }
-
D - Principio de Inversión de Dependencia (Dependency Inversion Principle): Las dependencias deben ir desde los módulos de alto nivel hacia los módulos de bajo nivel. Utilizamos la inyección de dependencias para implementar este principio.
- Inyeccion de dependencias desde
Program.cs
:
// Builder var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); // Register database context builder.Services.AddSingleton<MongoDbContext>(); // Register application services builder.Services.AddScoped<IElectronicBillService, ElectronicBillService>(); builder.Services.AddScoped<IPaymentCodeService, PaymentCodeService>(); builder.Services.AddScoped<IPaymentService, PaymentService>(); builder.Services.AddScoped<IPayerService, PayerService>(); // Register domain services builder.Services.AddScoped<IElectronicBillDomainService, ElectronicBillDomainService>(); builder.Services.AddScoped<IPaymentCodeDomainService, PaymentCodeDomainService>(); builder.Services.AddScoped<IPaymentDomainService, PaymentDomainService>(); // Register repositories builder.Services.AddScoped<IPaymentRepository, PaymentRepository>(); builder.Services.AddScoped<IElectronicBillRepository, ElectronicBillRepository>(); builder.Services.AddScoped<IPaymentCodeRepository, PaymentCodeRepository>(); builder.Services.AddScoped<IPayerRepository, PayerRepository>();
- Uso en controladores:
public interface IAdminController { Task<IActionResult> GetAllStudents(); } [Route("api/[controller]")] [ApiController] public class AdminController : ControllerBase, IAdminController { private readonly IAdminService _adminService; // inyeccion de dependencia public AdminController(IAdminService adminService) { _adminService = adminService; } [HttpPost("register/student")] public async Task<IActionResult> RegisterStudent([FromBody] StudentDto student) { var createdStudent = await _adminService.CreateStudentAsync(student); return StatusCode(createdStudent.StatusCode, createdStudent); } [HttpPost("register/admin")] public async Task<IActionResult> RegisterAdmin([FromBody] AdminDto userDTO) { var response = await _adminService.CreateAdminAccountAsync(userDTO); return StatusCode(response.StatusCode, response); } }
- Inyeccion de dependencias desde
-
Requisitos del sistema:
- Sistema operativo compatible (Windows, macOS, Linux).
.NET 8
SDK instalado.Docker
y Docker Compose instalados (para despliegue en contenedores).MySQL
(Base de datos relacional disponible de manera local o en la nube)MongoDB
(Base de datos no relacional disponible de manera local o en la nube)
-
Software necesario:
Git
para el control de versiones.Visual Studio Code
y C# Dev Kit (extensiones para trabajar en el entorno de .NET).- Herramientas de línea de comandos (bash, PowerShell, etc.).
mkdir users-microservice && cd users-microservice
dotnet new sln
Esta sección crea una nueva solución .NET en un directorio específico.
mkdir src && cd src && dotnet new web
Esta sección crea un nuevo proyecto web dentro de la carpeta src
.
mkdir test && cd test && dotnet new nunit
Esta sección crea un nuevo proyecto de pruebas usando NUnit dentro de la carpeta test
.
- Vincular el Proyecto de Pruebas con el Proyecto Fuente:
dotnet add reference ../src/src.csproj
- Navegar a la raíz del
proyecto base
. - Vincular Proyecto Fuente con el Proyecto Base:
dotnet sln add src/src.csproj
- Vincular Proyecto de Pruebas con el Proyecto Base:
cd .. dotnet sln add test/test.csproj
En src/
:
EntityFramework Core
para ejecutar comandos de dotnet ef en CLI:dotnet tool install --global dotnet-ef
EntityFrameworkCore.Design
para migraciones:dotnet add package Microsoft.EntityFrameworkCore.Design --version 9.0.0-preview.1.24081.2
- Dependencia
Pomelo.EntityFrameworkCore.MySql
para MySQL:dotnet add package Pomelo.EntityFrameworkCore.MySql --version 9.0.0-preview.1
- Dependencia
MongoDB.Driver
para MongoDB:dotnet add package MongoDB.Driver --version 2.28.0
- Dependencia
Microsoft.AspNetCore.Authentication.JwtBearer
para tokenización JWT:dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 8.0.5
En test/
:
coverlet.collector
para reportes de cobertura:dotnet add package coverlet.collector --version 6.0.2
- Dependencia
Moq
para mocking:dotnet add package Moq --version 4.20.70
Navegar a la carpeta src/
:
cd src/
Añadir una nueva migración:
dotnet ef migrations add NameMigrate
Actualizar la base de datos con la nueva migración:
dotnet ef database update
dotnet run --project src/src.csproj
dotnet test
dotnet build src/src.csproj --configuration Release
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=../../coverage # no funciona
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover --output coverage # funciona pero con advertencias
Las pruebas han sido diseñadas para asegurar la calidad del código y la funcionalidad del microservicio de gestión de matrículas. Se incluyen pruebas unitarias, pruebas de integración y pruebas funcionales. Las pruebas cubren escenarios de autenticación, manejo de usuarios, gestión de cursos, y más.
mkdir test && cd test && dotnet new nunit
dotnet add reference ../src/src.csproj
coverlet.collector
para reportes de cobertura:dotnet add package coverlet.collector --version 6.0.2
- Dependencia
Moq
para mocking:dotnet add package Moq --version 4.20.70
dotnet test
Para obtener reportes de cobertura de código, ejecutar:
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover --output coverage
-
Crear el Dockerfile: Asegúrate de que el Dockerfile se encuentra en la raíz del proyecto.
# Utilizar la imagen oficial de .NET como imagen base FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base WORKDIR /app EXPOSE 80 # Utilizar la imagen oficial de .NET SDK como imagen base para compilación FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build WORKDIR /src COPY ["src/YourProject.csproj", "src/"] RUN dotnet restore "src/YourProject.csproj" COPY . . WORKDIR "/src/src" RUN dotnet build "YourProject.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "YourProject.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "YourProject.dll"]
-
Construir la imagen de Docker: Ejecuta el siguiente comando en la terminal desde la raíz del proyecto donde se encuentra el Dockerfile.
docker build -t yourproject-image .
-
Crear y ejecutar un contenedor: Usa el siguiente comando para crear y ejecutar un contenedor a partir de la imagen recién creada.
docker run -d -p 8080:80 --name yourproject-container yourproject-image
Para el entorno de desarrollo, puedes utilizar Docker Compose para gestionar múltiples contenedores. Crea un archivo docker-compose.yml
con el siguiente contenido:
version: '3.4'
services:
yourproject:
image: yourproject-image
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:80"
environment:
- ASPNETCORE_ENVIRONMENT=Development
Luego, ejecuta:
docker-compose up
Para el entorno de pruebas, puedes modificar el archivo docker-compose.yml
para incluir servicios adicionales como bases de datos o servicios de prueba.
version: '3.4'
services:
yourproject:
image: yourproject-image
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:80"
environment:
- ASPNETCORE_ENVIRONMENT=Testing
db:
image: mysql:latest
environment:
MYSQL_ROOT_PASSWORD: yourpassword
MYSQL_DATABASE: yourdatabase
ports:
- "3306:3306"
Ejecuta:
docker-compose -f docker-compose.yml up --build
Para el entorno de producción, asegúrate de que todas las configuraciones y secretos están correctamente configurados. Puedes usar servicios de orquestación como Kubernetes para gestionar el despliegue a escala.
version: '3.4'
services:
yourproject:
image: yourproject-image
build:
context: .
dockerfile: Dockerfile
ports:
- "80:80"
environment:
- ASPNETCORE_ENVIRONMENT=Production
Ejecuta:
docker-compose -f docker-compose.prod.yml up --build -d
La integración continua se gestiona a través de Jenkins, automatizando la construcción, pruebas y análisis del código. El pipeline está configurado para ejecutar las siguientes etapas de manera paralela para cada microservicio:
- Compilación: Se utiliza
dotnet build
para compilar el código de cada microservicio. - Publicación:
dotnet publish
genera los artefactos necesarios para el despliegue.
- Análisis Estático: Realiza un análisis de calidad del código, cobertura, y detecta vulnerabilidades mediante SonarCloud.
- Ejecución de Pruebas: Las colecciones de Postman se ejecutan con Newman CLI para validar los endpoints de cada microservicio.
- Evaluación de Carga: Ejecuta planes de prueba de JMeter para medir el rendimiento y detectar cuellos de botella.
- Escaneo de Vulnerabilidades: Utiliza OWASP ZAP para identificar problemas de seguridad en las aplicaciones.
- Construcción y Despliegue: Se crean imágenes Docker para cada microservicio y se gestionan los contenedores para desarrollo, pruebas y producción.
Cada etapa del pipeline se ejecuta en paralelo para los diferentes microservicios, asegurando un proceso de integración continua eficiente.
This project is licensed under Creative Commons Atribución-NoComercial-CompartirIgual 4.0 Internacional:
[1] W. by Iamprovidence, “Backend side architecture evolution (N-layered, DDD, Hexagon, Onion, Clean Architecture)”, Medium, 27-jun-2023. [En línea]. Disponible en: https://medium.com/@iamprovidence/backend-side-architecture-evolution-n-layered-ddd-hexagon-onion-clean-architecture-643d72444ce4.
[2] C. Ramalingam, “Building domain driven microservices - Walmart global tech blog - medium”, Walmart Global Tech Blog, 01-jul-2020. [En línea]. Disponible en: https://medium.com/walmartglobaltech/building-domain-driven-microservices-af688aa1b1b8.
[3] J. Loscalzo, “Domain Driven Design: principios, beneficios y elementos — Segunda Parte”, Medium, 18-jun-2018. [En línea]. Disponible en: https://medium.com/@jonathanloscalzo/domain-driven-design-principios-beneficios-y-elementos-segunda-parte-337d77dc8566.
[4] P. Martinez, “Domain-Driven Design: Everything you always wanted to know”, SSENSE-TECH, 15-may-2020. [En línea]. Disponible en: https://medium.com/ssense-tech/domain-driven-design-everything-you-always-wanted-to-know-about-it-but-were-afraid-to-ask-a85e7b74497a.