API REST moderna construida con .NET 10 y C# 14 siguiendo Clean Architecture y buenas prácticas de desarrollo backend.
| Componente | Tecnología |
|---|---|
| Framework | .NET 10 / C# 14 |
| Arquitectura | Clean Architecture (4 capas) |
| Base de datos | SQL Server 2025 (Docker) |
| ORM | Entity Framework Core 10 |
| CQRS | MediatR 14 |
| Validación | FluentValidation + pipeline behavior |
| Autenticación | JWT con Refresh Tokens (JsonWebTokenHandler) |
| Hashing | Argon2id |
| Logging | Serilog (consola + archivo + Seq opcional) |
| Documentación | Swagger / Swashbuckle |
| Rate Limiting | Fixed Window (60 req/min por IP) |
| Health Checks | GET /health — estado de la BD |
| Contenedores | Dockerfile multi-stage + Docker Compose |
| Tests unitarios | xUnit + FluentAssertions + NSubstitute |
| Tests integración | Testcontainers (SQL Server) + Respawn + WebApplicationFactory |
git clone https://github.com/jemartnz/sun.git
cd sunLa imagen oficial está publicada en ghcr.io/jemartnz/sun.
# Descargar la imagen
docker pull ghcr.io/jemartnz/sun:latest
# O una versión específica
docker pull ghcr.io/jemartnz/sun:v1.0.0Requiere SQL Server. Ejemplo mínimo:
docker run -d \
-p 8080:8080 \
-e ConnectionStrings__Sun="Server=host.docker.internal,1433;Database=SunDb;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=true" \
-e Jwt__Secret="RXN0YUVzVW5hQ2xhdmVTZWNyZXRhTXV5TGFyZ2FRdWVEZWJlVGVuZXJBbE1lbm9zMzJDYXJhY3RlcmVzIQ==" \
ghcr.io/jemartnz/sun:latestVer .env.example para todas las variables de entorno disponibles.
Levanta la API, SQL Server y Seq en un solo comando:
docker compose up -d| Servicio | URL |
|---|---|
| API | http://localhost:8080 |
| Swagger UI | http://localhost:8080/swagger |
| Health Check | http://localhost:8080/health |
| Seq (logs) | http://localhost:8081 |
Requisitos previos:
# 1. Levantar SQL Server
docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=YourStrong!Passw0rd" \
-p 1433:1433 --name sqlserver \
-d mcr.microsoft.com/mssql/server:2025-latest
# 2. Ejecutar la API (aplica migraciones automáticamente)
dotnet run --project src/ApiLa API estará disponible en:
- HTTP: http://localhost:5000
- Swagger UI: http://localhost:5000/swagger/
- Health Check: http://localhost:5000/health
| Método | Ruta | Descripción |
|---|---|---|
| POST | /api/auth/register |
Registro. Retorna token + refreshToken |
| POST | /api/auth/login |
Login. Retorna token + refreshToken |
| POST | /api/auth/refresh |
Renueva el JWT con un refresh token (rotación) |
| POST | /api/auth/revoke |
Revoca un refresh token (logout) |
| Método | Ruta | Rol | Descripción |
|---|---|---|---|
| GET | /api/users |
Any | Listar usuarios paginados |
| GET | /api/users/me |
Any | Usuario autenticado actual |
| GET | /api/users/{id} |
Any | Usuario por Id |
| PUT | /api/users/{id} |
Any | Actualizar usuario |
| PUT | /api/users/{id}/address |
Any | Actualizar dirección |
| DELETE | /api/users/{id} |
Admin | Eliminar usuario |
| Método | Ruta | Descripción |
|---|---|---|
| GET | /api/products |
Listar productos. ?page, ?pageSize, ?name, ?minPrice, ?maxPrice, ?sortBy, ?sortOrder |
| GET | /api/products/{id} |
Producto por Id |
| POST | /api/products |
Crear producto |
| PUT | /api/products/{id} |
Actualizar producto |
| PATCH | /api/products/{id}/stock |
Actualizar stock |
| DELETE | /api/products/{id} |
Eliminar producto |
| Método | Ruta | Descripción |
|---|---|---|
| POST | /api/orders |
Crear orden — reduce stock automáticamente |
| Método | Ruta | Descripción |
|---|---|---|
| GET | /health |
Estado de la API y conexión a BD |
Registro:
curl -X POST http://localhost:5000/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"firstName": "Juan",
"lastName": "Pérez",
"email": "juan@email.com",
"password": "MiPassword123"
}'Respuesta:
{
"token": "eyJ...",
"refreshToken": "base64string...",
"userId": "guid...",
"email": "juan@email.com"
}Renovar JWT (cuando expira en 15 min):
curl -X POST http://localhost:5000/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{ "refreshToken": "base64string..." }'Crear orden:
curl -X POST http://localhost:5000/api/orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"items": [
{ "productId": "guid...", "quantity": 2 }
]
}'Sun/
├── Dockerfile ← Build multi-stage (.NET 10, usuario no-root)
├── docker-compose.yml ← API + SQL Server + Seq
├── .env.example ← Variables de entorno documentadas
├── src/
│ ├── Domain/ ← Entidades, Value Objects, Result Pattern, Enums
│ │ ├── Commons/ ← BaseEntity, Error, Result, Result<T>
│ │ ├── Entities/ ← User, Product, Order, OrderItem, RefreshToken
│ │ ├── Enums/ ← UserRole, OrderStatus
│ │ └── ValueObjects/ ← Email, Password, Address, Price
│ │
│ ├── Application/ ← Casos de uso (CQRS), DTOs, Interfaces, Validators
│ │ ├── Behaviors/ ← ValidationBehavior (MediatR pipeline → HTTP 422)
│ │ ├── Common/ ← PagedRequest, PagedResponse<T>
│ │ ├── DTOs/ ← AuthResponse, UserResponse, ProductResponse, OrderResponse
│ │ ├── Features/ ← Auth, Users, Products, Orders (Commands + Queries + Handlers)
│ │ └── Interfaces/ ← IUserRepository, IProductRepository, IOrderRepository, IRefreshTokenRepository, etc.
│ │
│ ├── Infrastructure/ ← EF Core, Repositorios, JWT, Argon2id, Health Check
│ │ ├── Persistence/ ← AppDbContext, Configs, Repositories, DatabaseHealthCheck, Migrations
│ │ └── Security/ ← Argon2PasswordHasher, JwtTokenGenerator
│ │
│ └── Api/ ← Controllers, Middleware, Program.cs
│ ├── Controllers/ ← Auth, Users, Products, Orders
│ ├── Extensions/ ← ResultExtensions (Result → HTTP response)
│ └── Middlewares/ ← ExceptionMiddleware (422 + 500)
│
└── tests/
├── Domain.Tests/ ← 66 tests — Value Objects, Entidades, Result Pattern
├── Application.Tests/ ← 24 tests — Handlers con mocks (NSubstitute)
└── Api.Tests/ ← 38 tests — Integración con SQL Server real (Testcontainers)
- Clean Architecture — Separación estricta en 4 capas con dependencias hacia adentro
- CQRS — Commands y Queries separados con MediatR 14
- Result Pattern — Errores de negocio como valores de retorno, sin excepciones
- Value Objects — Email, Password, Address, Price con validación encapsulada
- Aggregate Root — Order gestiona OrderItems; lógica de negocio en el dominio
- Repository Pattern — Interfaces en Application, implementaciones en Infrastructure
- Refresh Token Rotation — Cada uso del refresh token emite uno nuevo e invalida el anterior
- ValidationBehavior — FluentValidation como pipeline de MediatR → HTTP 422
- Argon2id — Hashing de contraseñas (salt 16 bytes, hash 32 bytes, 64MB RAM, 3 iteraciones)
- JWT de corta duración — Expiry configurable (15 min por defecto)
- Refresh Tokens — Opacos, de larga duración (7 días), con rotación automática
- RBAC — Roles
UseryAdminincluidos en el JWT.FallbackPolicyrequiere autenticación por defecto - Rate Limiting — 60 requests por minuto por IP (Fixed Window)
- Mensajes de error genéricos en login para prevenir enumeración de emails
| Feature | Detalle |
|---|---|
| Request logging | UseSerilogRequestLogging() — método, ruta, status, duración |
| Archivo de logs | logs/log-{fecha}.txt — rotación diaria, retención 30 días |
| Seq | Sink opcional para búsqueda de logs estructurados. Activar con Seq:ServerUrl en config |
| Health Check | GET /health — verifica conectividad con SQL Server |
En producción las claves sensibles se inyectan via variables de entorno (ver .env.example):
| Variable | Descripción |
|---|---|
ConnectionStrings__Sun |
Connection string de SQL Server |
Jwt__Secret |
Clave para firmar tokens (Base64, ≥32 chars) |
Jwt__ExpiryMinutes |
Duración del JWT (default: 15) |
Jwt__RefreshTokenExpiryDays |
Duración del refresh token (default: 7) |
Cors__AllowedOrigins__0 |
Origen permitido en producción |
Seq__ServerUrl |
URL del servidor Seq (vacío = deshabilitado) |
AdminSeed__Email |
Email del usuario admin inicial |
AdminSeed__Password |
Contraseña del usuario admin inicial |
# Todos los tests
dotnet test
# Por proyecto
dotnet test tests/Domain.Tests
dotnet test tests/Application.Tests
dotnet test tests/Api.Tests # requiere Docker (Testcontainers)| Proyecto | Tests | Cobertura |
|---|---|---|
| Domain.Tests | 66 | Value Objects, Entidades, Result Pattern |
| Application.Tests | 24 | Handlers con repositorios mockeados |
| Api.Tests | 38 | Endpoints completos con SQL Server real |
| Total | 128 |
# Ejecutar la API
dotnet run --project src/Api
# Agregar migración
dotnet ef migrations add NombreMigracion \
--project src/Infrastructure \
--startup-project src/Api \
--output-dir Persistence/Migrations
# Levantar todo con Docker Compose
docker compose up -d
# Ver logs de la API
docker compose logs api -f| Archivo | Descripción |
|---|---|
CLAUDE.md |
Contexto técnico completo — arquitectura, patrones, decisiones clave |
ROADMAP.md |
Estado de features: completadas y planificadas |
.env.example |
Variables de entorno requeridas para producción |
MIT — ver LICENSE para más detalles.