Skip to content

abengl/StudentMS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 

Repository files navigation

📊 Student API — Reto Técnico BBVA

API REST desarrollada con Java 17 + Spring Boot 3 para procesar dos archivos de texto de formato fijo, cruzar su información y retornar al estudiante o estudiantes con la nota más alta.


🧱 Arquitectura y decisiones de diseño

El proyecto sigue una arquitectura en capas con separación estricta de responsabilidades:

com.alessandragodoy.studentms/
│
├── 🌐 controller/
│   └── StudentController          → Adaptador HTTP. Implementa StudentApi (contract-first via OpenAPI)
│
├── ⚙️  service/
│   ├── IStudentService            → Contrato del servicio
│   └── impl/
│       └── StudentServiceImpl     → Orquesta parsing y nota máxima
│
├── 📄 parser/
│   ├── IFileParser<T>             → Abstracción genérica (permite nuevos formatos sin modificar código existente)
│   └── impl/
│       ├── E1FileParser           → Parsea archivo de notas      
│       └── E2FileParser           → Parsea archivo de sexo       
│
├── 📦 dto/
│   ├── E1Record                   → Record de entrada (nota)
│   ├── E2Record                   → Record de entrada (sexo)
│   ├── HighestGradeResultDTO      → DTO de respuesta al cliente
│   └── CustomErrorResponse        → Estructura uniforme de errores
│
├── ⚠️  exception/
│   ├── EmptyFileException         → Archivo vacío
│   ├── FileParsingException       → Error estructural / I/O
│   ├── FileValidationException    → Dato inválido dentro del archivo
│   └── GlobalExceptionHandler     → Manejo centralizado con @ControllerAdvice
│
├── 🛠️  util/
│   └── ValidationUtil             → Validaciones reutilizables
│
├── ⚙️  config/
│   └── OpenApiConfig              → Configuración de Swagger UI
│
└── 🗄️  model/
    └── Student                    → Entidad JPA (persistencia futura)

Decisiones clave:

  • Contract-first: el contrato REST (StudentApi) se generó desde el spec OpenAPI antes de implementar el controller, permitiendo desarrollo desacoplado y tests independientes de la implementación.
  • TDD: los parsers y el service fueron desarrollados partiendo de los tests — los casos de prueba definen el comportamiento esperado antes que el código.
  • Parsers genéricos con OCP: IFileParser<T> permite agregar nuevos formatos de archivo sin modificar código existente.
  • Modelo de persistencia preparado: la entidad Student y el repositorio JPA/H2 están incluidas como base lista para evolucionar hacia persistencia real sin refactoring mayor.

⚙️ Requisitos

Herramienta Versión mínima
Java 17
Maven 3.9+

No se requiere instalar base de datos ni ninguna dependencia externa. La aplicación levanta directamente con mvn spring-boot:run.


🚀 Ejecución

# Clonar el repositorio
git clone https://github.com/abengl/StudentMS.git
cd studentms

# Compilar y ejecutar
mvn spring-boot:run

La aplicación levanta en http://localhost:8080


🧪 Tests

# Ejecutar toda la suite
mvn test

Cobertura por capa:

  • E1FileParserTest — Unit tests del parser de notas: formato fijo, largo de línea, validaciones de tipo de doc, número de doc, rango de nota, caracteres no numéricos, líneas en blanco.
  • E2FileParserTest — Unit tests del parser de sexo: mismas validaciones estructurales + género válido (M/F), case-sensitive.
  • StudentServiceImplTest — Unit tests del servicio: cruce de archivos, nota máxima, empates, registros sin match, duplicados en E1 y E2.
  • StudentControllerTest — Tests de integración de capa web (@WebMvcTest): archivos vacíos, faltantes, Content-Type incorrecto, errores de validación, estructura del error response.

🌐 Endpoint

POST /api/v1/students/highest-grade

Procesa los archivos E1 y E2 y retorna al estudiante o estudiantes con la nota más alta. En caso de empate, se retornan todos los estudiantes con esa nota.

Requestmultipart/form-data

Campo Tipo Descripción
e1 file Archivo de notas (formato fijo: tipo_doc[1] + num_doc[8] + nota[2]) — 11 caracteres por línea
e2 file Archivo de sexo (formato fijo: tipo_doc[1] + num_doc[8] + sexo[1]) — 10 caracteres por línea

Response200 OK

[
  {
    "documentType": "1",
    "documentNumber": "12345678",
    "gender": "F",
    "grade": 20
  }
]

Errores

Status Causa
400 Archivo vacío, campo faltante, o dato inválido (tipo de doc, número de doc, nota fuera de rango, género inválido)
415 Content-Type incorrecto (se espera multipart/form-data)
422 Error de formato estructural: largo de línea incorrecto, nota no numérica, o error de I/O

Los errores retornan la siguiente estructura:

{
  "timestamp": "2026-05-11T08:00:00",
  "message": "Line 2 has invalid length: expected 11, got 10",
  "path": "/api/v1/students/highest-grade"
}

📖 Documentación interactiva

Swagger UI disponible en: http://localhost:8080/swagger-ui.html


📁 Formato de archivos de entrada

E1 — Notas (11 caracteres por línea, sin espacios)

[tipo_doc: 1 char][num_doc: 8 chars][nota: 2 chars]

Valores válidos:

  • tipo_doc: 1 (DNI), 2 (CE), 3 (Pasaporte)
  • num_doc: exactamente 8 dígitos
  • nota: número entero entre 00 y 20

Ejemplo:

11234567818
21111111120

E2 — Sexo (10 caracteres por línea, sin espacios)

[tipo_doc: 1 char][num_doc: 8 chars][sexo: 1 char]

Valores válidos:

  • sexo: F (femenino) o M (masculino) — case-sensitive

Ejemplo:

112345678F
211111111M

Las líneas en blanco son ignoradas en ambos archivos.


🔀 Lógica de cruce

El servicio cruza E1 y E2 usando la combinación tipo_doc + num_doc como clave:

  1. Se construye un mapa de sexo desde E2 (tipo_doc + num_doc → gender). En caso de duplicados, se conserva el primer registro.
  2. Se filtran los registros de E1 que tienen match en ese mapa.
  3. Se calcula la nota máxima entre los registros cruzados.
  4. Se retornan todos los registros con esa nota máxima (soporte de empates).

Si ningún registro de E1 tiene match en E2, se lanza NoSuchElementException.


🤖 Uso de IA en el desarrollo

La IA fue utilizada como herramienta de apoyo en etapas específicas, con revisión y criterio propio en cada decisión:

Etapa Uso
Análisis del problema Descomposición del scope e identificación de edge cases (líneas malformadas, registros sin match, empates, duplicados) que pueden omitirse en una primera lectura
Diseño de arquitectura Evaluación de dependencias y tecnologías para justificar decisiones con criterio propio
Generación de casos de prueba Sugerencia de escenarios de test para los parsers y el servicio
Boilerplate y configuración Scaffolding inicial de application.yml y configuración de Spring revisado manualmente
Code review asíncrono Revisión de implementaciones antes de commit para detectar inconsistencias en nomenclatura y manejo de errores

📫 Contacto

Alessandra Godoy

About

API REST para procesar dos archivos de texto de formato fijo, cruzar su información y retornar al estudiante o estudiantes con la nota más alta.

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages