Proyecto educativo de e-commerce construido con Spring Boot.
Sistema de gestión de clientes, productos, compras y sucursales con API REST, HATEOAS y Swagger.
📚 Proyecto educativo — Este repositorio forma parte de mi formación temprana en DUOC UC (Instituto Profesional). Las prácticas aquí reflejadas corresponden al momento de desarrollo y pueden no representar estándares actuales.
Ecomarket es un proyecto ficticio con fines educativos que modela el backend de una plataforma de e-commerce. Implementa una API REST completa sobre Spring Boot con documentación OpenAPI (Swagger UI), enlaces HATEOAS y persistencia en MySQL mediante Spring Data JPA. El dominio cubre clientes, productos, compras, empleados, sucursales y la división geográfica (regiones y comunas).
⚠️ Nota: Proyecto ficticio con fines educativos. No está destinado a uso en producción.
- API REST CRUD para 9 entidades de dominio, versionada bajo
/api/v1 - Arquitectura por capas: controladores → servicios → repositorios
- Hipermedia (HATEOAS) en todas las respuestas de recursos
- Documentación interactiva con Swagger / OpenAPI (
springdoc-openapi) - Persistencia con Spring Data JPA + MySQL
- Migraciones de base de datos versionadas con Flyway
- Validación de datos con Jakarta Validation (
@Valid) - Lógica de negocio en servicios: validación de RUT chileno, control de stock, cálculo de totales
- Manejo centralizado de errores con excepciones personalizadas (
ResourceNotFoundException,InsufficientStockException,ValidationException) - Logging con SLF4J en toda la capa de servicios
- Datos de prueba generados con Datafaker (carga idempotente)
- Pruebas unitarias (servicios) y de controlador (web) con JUnit 5 + Mockito
- Soporte para Docker y Docker Compose (MySQL 8 + aplicación)
- Redirecciones de conveniencia hacia Swagger UI (
/,/docs,/api/v1)
| Tecnología | Versión | Uso |
|---|---|---|
| Java | 17 | Lenguaje base |
| Spring Boot | 3.3.13 | Framework principal |
| Spring Data JPA | — | Persistencia / repositorios |
| Spring HATEOAS | — | Hipermedia en la API |
| springdoc-openapi | 2.5.0 | Documentación Swagger UI |
| Flyway | — | Migraciones de base de datos versionadas |
| MySQL Connector/J | — | Driver JDBC de MySQL |
| Jakarta Validation | — | Validación de datos (@Valid) |
| SLF4J / Logback | — | Logging |
| Lombok | — | Reducción de boilerplate |
| Datafaker | 2.1.0 | Generación de datos de prueba |
| JUnit 5 + Mockito | — | Pruebas unitarias y de controlador |
| Docker / Compose | — | Contenerización (MySQL 8 + app) |
| Maven | (wrapper incluido) | Gestión de build |
El proyecto sigue una arquitectura por capas típica de Spring Boot:
src/main/java/com/ecomarket/ecomarket/
├── controller/ # Controladores REST (HATEOAS + Swagger) — delegan en servicios
├── service/ # Lógica de negocio, validaciones y reglas de dominio
├── model/ # Entidades JPA con validación Jakarta
├── repository/ # Repositorios de Spring Data JPA (acceso a datos)
├── exception/ # Excepciones personalizadas y manejador global
├── config/ # Configuración (CORS)
└── util/ # Utilidades (validación de RUT chileno)
controller/— Controladores REST con enlaces HATEOAS y anotaciones OpenAPI. No acceden directamente a los repositorios: toda la operación se enruta a través de servicios.service/— Servicios con la lógica de negocio (CRUD completo, validación de RUT, control de stock, cálculo de totales) y logging SLF4J.model/— Entidades JPA que mapean el dominio a la base de datos, con anotaciones de validación (@NotBlank,@NotNull,@Size,@Min,@Pattern, etc.).repository/— Interfaces de Spring Data JPA para acceso a datos.exception/— Excepciones personalizadas (ResourceNotFoundException,InsufficientStockException,ValidationException) yGlobalExceptionHandler(@ControllerAdvice) que centraliza las respuestas de error.util/— Clases de utilidad (cálculo y validación de dígito verificador de RUN).db/migration/— Migraciones versionadas de Flyway para el esquema de base de datos.
El sistema modela un e-commerce con 9 entidades y sus relaciones:
| Entidad | Clave primaria | Relaciones |
|---|---|---|
| Cliente | run |
— |
| Producto | idProducto |
— |
| Compra | idCompra |
N→1 Cliente · N→1 Sucursal · N→1 Empleado · 1→N Detalle |
| Detalle | idDetalle |
N→1 Compra · N→1 Producto |
| Empleado | runEmpleado |
N→1 Departamento · 1→N Compra |
| Sucursal | idSucursal |
N→1 Comuna · 1→N Compra |
| Departamento | idDepartamento |
1→N Empleado |
| Región | idRegion |
1→N Comuna |
| Comuna | idComuna |
N→1 Región · 1→N Sucursal |
La API se sirve bajo el prefijo /api/v1. Todas las operaciones devuelven recursos
con enlaces HATEOAS. La base URL es http://localhost:9090.
| Método | Ruta | Descripción |
|---|---|---|
| GET | /api/v1/clientes |
Obtener todos los clientes |
| GET | /api/v1/clientes/{run} |
Obtener cliente por RUN |
| POST | /api/v1/clientes |
Crear un nuevo cliente |
| PUT | /api/v1/clientes/{run} |
Actualizar un cliente existente |
| DELETE | /api/v1/clientes/{run} |
Eliminar un cliente por RUN |
| Método | Ruta | Descripción |
|---|---|---|
| GET | /api/v1/compras |
Obtener todas las compras |
| GET | /api/v1/compras/{idCompra} |
Obtener compra por ID |
| GET | /api/v1/compras/{idCompra}/total |
Obtener el total de una compra |
| POST | /api/v1/compras |
Crear una nueva compra |
| PUT | /api/v1/compras/{idCompra} |
Actualizar compra por ID |
| DELETE | /api/v1/compras/{idCompra} |
Eliminar compra por ID |
| Método | Ruta | Descripción |
|---|---|---|
| GET | /api/v1/detalles |
Obtener todos los detalles |
| GET | /api/v1/detalles/{idDetalle} |
Obtener detalle por ID |
| POST | /api/v1/detalles |
Crear un nuevo detalle |
| PUT | /api/v1/detalles/{idDetalle} |
Actualizar detalle por ID |
| DELETE | /api/v1/detalles/{idDetalle} |
Eliminar detalle por ID |
| Método | Ruta | Descripción |
|---|---|---|
| GET | /api/v1/productos |
Obtener todos los productos |
| GET | /api/v1/productos/{idProducto} |
Obtener producto por ID |
| POST | /api/v1/productos |
Crear un nuevo producto |
| PUT | /api/v1/productos/{idProducto} |
Actualizar producto por ID |
| DELETE | /api/v1/productos/{idProducto} |
Eliminar producto por ID |
| Método | Ruta | Descripción |
|---|---|---|
| GET | /api/v1/empleados |
Obtener todos los empleados |
| GET | /api/v1/empleados/{runEmpleado} |
Obtener empleado por RUN |
| GET | /api/v1/empleados/departamento/{idDepartamento} |
Empleados por departamento |
| POST | /api/v1/empleados |
Crear un nuevo empleado |
| PUT | /api/v1/empleados/{runEmpleado} |
Actualizar empleado por RUN |
| DELETE | /api/v1/empleados/{runEmpleado} |
Eliminar empleado por RUN |
| Método | Ruta | Descripción |
|---|---|---|
| GET | /api/v1/sucursales |
Obtener todas las sucursales |
| GET | /api/v1/sucursales/{idSucursal} |
Obtener sucursal por ID |
| GET | /api/v1/sucursales/comuna/{idComuna} |
Sucursales por comuna |
| POST | /api/v1/sucursales |
Crear una nueva sucursal |
| PUT | /api/v1/sucursales/{idSucursal} |
Actualizar sucursal por ID |
| DELETE | /api/v1/sucursales/{idSucursal} |
Eliminar sucursal por ID |
| Método | Ruta | Descripción |
|---|---|---|
| GET | /api/v1/departamentos |
Obtener todos los departamentos |
| GET | /api/v1/departamentos/{idDepartamento} |
Obtener departamento por ID |
| POST | /api/v1/departamentos |
Crear un nuevo departamento |
| PUT | /api/v1/departamentos/{idDepartamento} |
Actualizar departamento por ID |
| DELETE | /api/v1/departamentos/{idDepartamento} |
Eliminar departamento por ID |
| Método | Ruta | Descripción |
|---|---|---|
| GET | /api/v1/regiones |
Obtener todas las regiones |
| GET | /api/v1/regiones/{idRegion} |
Obtener región por ID |
| POST | /api/v1/regiones |
Crear una nueva región |
| PUT | /api/v1/regiones/{idRegion} |
Actualizar región por ID |
| DELETE | /api/v1/regiones/{idRegion} |
Eliminar región por ID |
| Método | Ruta | Descripción |
|---|---|---|
| GET | /api/v1/comunas |
Obtener todas las comunas |
| GET | /api/v1/comunas/{idComuna} |
Obtener comuna por ID |
| GET | /api/v1/comunas/region/{idRegion} |
Comunas por región |
| POST | /api/v1/comunas |
Crear una nueva comuna |
| PUT | /api/v1/comunas/{idComuna} |
Actualizar comuna por ID |
| DELETE | /api/v1/comunas/{idComuna} |
Eliminar comuna por ID |
| Método | Ruta | Descripción |
|---|---|---|
| GET | / |
Redirección a la página principal |
| GET | /docs |
Redirección a Swagger UI |
| GET | /api/v1 |
Redirección alternativa a Swagger UI |
- Java 17
- Maven (o usar el wrapper
./mvnwincluido) - Una instancia de MySQL accesible (o usar Docker, ver más abajo)
# Clonar el repositorio
git clone https://github.com/VECTORG99/Ecomarket.git
cd Ecomarket
# (Opcional) dar permisos de ejecución al wrapper
chmod +x mvnw
# Levantar la aplicación
./mvnw spring-boot:runLa aplicación se inicia en http://localhost:9090.
El proyecto incluye un Dockerfile multi-etapa y un docker-compose.yml que levanta
MySQL 8 y la aplicación juntos:
# Construir y levantar todos los servicios (MySQL + app)
docker compose up --build
# La API queda disponible en http://localhost:9090
# MySQL expuesto en localhost:3306 (usuario: root, contraseña: root)Flyway aplica automáticamente las migraciones de src/main/resources/db/migration/ al
iniciar la aplicación, creando el esquema ecomarket_dev.
Una vez levantada la aplicación, la documentación interactiva de la API está disponible en:
http://localhost:9090/swagger-ui/index.html
También puedes acceder mediante las redirecciones de conveniencia
http://localhost:9090/docs o http://localhost:9090/api/v1.
La configuración se gestiona mediante perfiles de Spring en
src/main/resources/:
| Archivo | Perfil | Propósito |
|---|---|---|
application.properties |
— | Configuración base (nombre, perfil activo, puerto) |
application-dev.properties |
dev |
Conexión a MySQL de desarrollo |
application-test.properties |
test |
Conexión a MySQL de pruebas |
# application.properties
spring.application.name=ecomarket
spring.profiles.active=dev
server.port=9090Los perfiles dev y test definen la conexión a MySQL. Las credenciales se
encuentran como placeholders y deben configurarse para tu entorno local:
# application-dev.properties
spring.datasource.url=jdbc:mysql://localhost:3306/ecomarket_dev
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
# Flyway gestiona el esquema mediante migraciones versionadas
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
spring.flyway.baseline-on-migrate=trueℹ️ Esquema: El esquema de la base de datos se crea y versiona con Flyway (
src/main/resources/db/migration/V1__initial_schema.sql). Hibernate está en modovalidate, de modo que solo verifica que las entidades coincidan con el esquema migrado, sin modificarlo.
🔒 Seguridad: No se incluyen credenciales reales en el repositorio. Antes de ejecutar, configura
spring.datasource.usernameyspring.datasource.passwordcon los valores de tu instancia de MySQL (o mediante variables de entorno / un servidor de configuración externo). El usuariorootsin contraseña es únicamente el valor por defecto para entornos locales de desarrollo.
El proyecto incluye pruebas unitarias (servicios) y de controlador (web) para cada
entidad (*ServiceTest y *ControllerTest) usando JUnit 5 y Mockito. Las pruebas
cubren operaciones CRUD, validaciones, escenarios de error (404, 400, 409) y lógica
de negocio (stock insuficiente, RUT inválido, cálculo de totales).
*ServiceTest— pruebas unitarias puras con@ExtendWith(MockitoExtension.class), mockeando los repositorios. No requieren base de datos.*ControllerTest— pruebas de slice web con@WebMvcTest, mockeando los servicios y verificando respuestas HTTP (incluye elGlobalExceptionHandler).
# Ejecutar todas las pruebas
./mvnw test
# Compilar sin pruebas
./mvnw compileLas pruebas utilizan el perfil test (application-test.properties). Al ser slice tests
y pruebas unitarias, no necesitan una instancia de MySQL en ejecución.
Este proyecto está bajo la licencia MIT. Consulta el archivo LICENSE para más detalles.
Proyecto ficticio con fines educativos.