Nota: Se generarán credenciales de administrador y se enviarán de forma segura como parte de la entrega al mail: mouredev@gmail.com
Una API REST lista para producción diseñada para construir aplicaciones de descubrimiento y gestión de consejos prácticos. Lifehacking proporciona un backend completo para explorar consejos de vida diaria, organizarlos por categorías y gestionar favoritos de usuarios con transiciones fluidas de anónimo a autenticado.
Construido con .NET 10 y principios de Arquitectura Limpia. Este proyecto fue creado a partir de arielbvergara/clean-architecture — una plantilla reutilizable de Arquitectura Limpia desarrollada durante lecciones y convertida en plantilla. En ese proyecto podran ver los commits que se hicieron previos a la creacion de este proyecto.
🤖 Desarrollo asistido por IA: Kiro y Warp fue utilizado como asistente de IA durante todo el desarrollo de este proyecto.
- Aplicación Desplegada
- Slides de la presentacion del proyecto
- Descripción General del Proyecto
- Stack Tecnológico
- Proyectos Relacionados
- Características Principales
- Instalación y Ejecución
- Estructura del Proyecto
- Endpoints de la API
- Arquitectura
- Autenticación y Autorización
- Características de Seguridad
- Pruebas
- Guías de Desarrollo
- Roadmap
- Frontend: https://lifehacking.vercel.app/
- Backend API: https://slight-janet-lifehacking-ce47cbe0.koyeb.app/
Lifehacking Master Slides Presentation
Lifehacking Tips API es una solución backend completa y robusta que permite construir aplicaciones donde los usuarios pueden descubrir, organizar y gestionar consejos prácticos para mejorar su vida diaria. La API está diseñada con arquitectura moderna y mejores prácticas de la industria, proporcionando una base sólida para aplicaciones web y móviles.
La API permite a los desarrolladores crear aplicaciones donde los usuarios pueden:
- Descubrir consejos a través de búsqueda avanzada, filtrado por categorías y etiquetas (sin necesidad de autenticación)
- Guardar favoritos con sincronización automática entre almacenamiento local y persistencia del lado del servidor
- Gestionar contenido mediante una interfaz administrativa completa para consejos y categorías
- Administrar usuarios con autenticación Firebase, control de acceso basado en roles y gestión de cuentas de autoservicio
El sistema está diseñado para soportar tres tipos de usuarios con diferentes niveles de acceso:
- Usuarios Anónimos - Acceso completo de lectura con favoritos del lado del cliente
- Usuarios Autenticados - Favoritos persistentes con fusión automática del almacenamiento local
- Administradores - Capacidades completas de gestión de contenido y usuarios
El proyecto sigue los principios de Arquitectura Limpia (Clean Architecture) y Diseño Dirigido por el Dominio (Domain-Driven Design), garantizando:
- Separación clara de responsabilidades entre capas
- Independencia del dominio de negocio respecto a frameworks y tecnologías externas
- Código mantenible, testeable y escalable
- Facilidad para agregar nuevas funcionalidades sin afectar el código existente
| Tecnología | Propósito |
|---|---|
| .NET 10 + Arquitectura Limpia | API Web con capas Domain, Application, Infrastructure y WebAPI |
| Firebase Authentication | Validación de tokens JWT Bearer y gestión de identidad |
| Firebase Cloud Firestore | Base de datos NoSQL principal |
| AWS S3 | Almacenamiento de imágenes de categorías |
| AWS CloudFront | CDN para entrega de imágenes |
| Docker & Docker Compose | Despliegue containerizado |
| Koyeb | Plataforma de despliegue en la nube |
| Dependabot | Actualizaciones automáticas semanales de dependencias |
| GitHub Actions | Pipeline CI (build, test, lint, escaneo de seguridad) y revisión de código |
| Kiro | Asistente de IA utilizado durante el desarrollo |
| Warp | Asistente de IA utilizado durante el desarrollo |
| Sentry | Seguimiento de errores y monitoreo de rendimiento |
| Swagger / OpenAPI | Documentación interactiva de la API |
| Github Copilot | Asistente de IA utilizado para revisión de código |
Frontend (lifehacking-app)
| Tecnología | Propósito |
|---|---|
| Next.js 16 | Framework frontend basado en React |
| Google Stitch | Diseño UI/UX |
| Firebase Authentication | Autenticación e identidad |
| Vercel | Despliegue del frontend |
| Sentry.io | Monitoreo y seguimiento de errores |
| Docker | Despliegue containerizado |
| Dependabot | Actualizaciones automáticas semanales de dependencias |
| GitHub Actions | Pipeline CI y revisión de código |
| Kiro | Asistente de IA utilizado durante el desarrollo |
| Github Copilot | Asistente de IA utilizado para revisión de código |
| Proyecto | Descripción | Despliegue |
|---|---|---|
| lifehacking-app | Frontend — Next.js 16, diseño Google Stitch, Firebase, Docker, Vercel | Vercel |
| lifehacking (este repositorio) | API Backend — .NET 10, Arquitectura Limpia, Firebase, Docker, AWS | Koyeb |
- Exploración de consejos con búsqueda y filtrado avanzado (por categoría, etiquetas, término de búsqueda)
- Visualización detallada de información de consejos incluyendo instrucciones paso a paso
- Exploración de categorías con acceso a todas las categorías disponibles
- Ordenamiento flexible de resultados por fecha de creación, fecha de actualización o título
- Respuestas paginadas para rendimiento óptimo
- Gestión de favoritos del lado del cliente (almacenamiento local)
- Todas las capacidades de usuarios anónimos
- Favoritos persistentes almacenados del lado del servidor
- Fusión automática de favoritos locales en el primer inicio de sesión (sin duplicados)
- Sincronización entre dispositivos de favoritos
- Gestión de perfil de autoservicio (ver, actualizar nombre, eliminar cuenta)
- Todas las capacidades de usuarios autenticados
- Gestión completa del ciclo de vida de consejos (crear, actualizar, eliminar)
- Gestión de categorías con eliminación en cascada
- Administración de usuarios completa
- Creación de usuarios administradores con integración Firebase
- Panel de control con estadísticas en tiempo real y conteo de entidades
- Registro de auditoría para todas las acciones administrativas
- Arquitectura Limpia con separación clara de responsabilidades
- Caché en memoria con invalidación automática para optimización de rendimiento
- Eliminación suave (Soft Delete) para preservación de datos y auditoría
- Validación exhaustiva de entrada con respuestas de error detalladas
- IDs de correlación para trazabilidad de solicitudes en logs y sistemas de monitoreo
- Documentación interactiva con Swagger/OpenAPI
- Seguridad robusta con JWT, rate limiting, headers de seguridad y CORS configurable
Antes de comenzar, asegúrate de tener instalado:
- .NET SDK 10.0 o superior
- Docker y Docker Compose
- Un proyecto Firebase para autenticación (créalo en Firebase Console)
- Opcional: Un proyecto Sentry para monitoreo (regístrate en sentry.io)
La forma más rápida de ejecutar la API localmente con todas las dependencias configuradas.
Antes de ejecutar docker compose up, necesitas:
-
Archivo de credenciales de Firebase Admin SDK
- Descarga el archivo JSON de credenciales desde Firebase Console
- Ve a: Configuración del proyecto → Cuentas de servicio → Generar nueva clave privada
- Guarda el archivo como
firebase-adminsdk.jsonen~/secrets/
# Crear directorio si no existe mkdir -p ~/secrets # Copiar tu archivo de credenciales cp /ruta/a/tu/firebase-adminsdk.json ~/secrets/firebase-adminsdk.json
-
Configurar variables de entorno en docker-compose.yml (ya configuradas por defecto):
ASPNETCORE_ENVIRONMENT: Development- Entorno de desarrolloClientApp__Origin: "http://localhost:3000"- Origen del frontend para CORSGOOGLE_APPLICATION_CREDENTIALS: /app/firebase-adminsdk.json- Ruta a credenciales dentro del contenedor
-
Configurar Firebase en appsettings.json o variables de entorno
Opción A: Editar
lifehacking/WebAPI/appsettings.Development.json:{ "Firebase": { "ProjectId": "tu-proyecto-firebase", }, "Authentication": { "Authority": "https://securetoken.google.com/tu-proyecto-firebase", "Audience": "tu-proyecto-firebase" } }Opción B: Agregar variables de entorno en
docker-compose.yml:environment: Firebase__ProjectId: "tu-proyecto-firebase" Authentication__Authority: "https://securetoken.google.com/tu-proyecto-firebase" Authentication__Audience: "tu-proyecto-firebase"
Una vez configurado:
docker compose up --buildEsto realizará:
- Construcción de la imagen Docker con .NET 10
- Montaje del archivo de credenciales Firebase
- Inicio del contenedor WebAPI
- Configuración de la API para usar Firebase/Firestore
- Exposición de la API en el puerto 8080
Una vez en ejecución:
- URL Base de la API:
http://localhost:8080 - Swagger UI:
http://localhost:8080/swagger(documentación interactiva de la API) - Health Check:
http://localhost:8080/health(si está configurado)
Para detener los servicios:
docker compose downNota importante: El proyecto está diseñado para usar Firebase/Firestore como base de datos. Docker Compose está configurado para conectarse automáticamente a tu proyecto Firebase.
Para iteración más rápida durante el desarrollo, ejecuta la API directamente usando el SDK de .NET:
# Compilar la solución
dotnet build lifehacking.slnx
# Ejecutar el proyecto WebAPI
dotnet run --project lifehacking/WebAPI/WebAPI.csprojLa API lee la configuración de lifehacking/WebAPI/appsettings.Development.json y variables de entorno, conectándose a Firebase/Firestore según la configuración establecida.
Para probar endpoints autenticados y de administrador, configura Firebase como tu proveedor de identidad:
-
Actualiza
appsettings.Development.json:{ "Authentication": { "Authority": "https://securetoken.google.com/<tu-firebase-project-id>", "Audience": "<tu-firebase-project-id>" }, "Firebase": { "ProjectId": "<tu-firebase-project-id>" } } -
Obtén un token de ID de Firebase:
- Autentica un usuario a través de Firebase (web, móvil o API REST)
- Extrae el token de ID de la respuesta de autenticación
-
Usa el token en las solicitudes de la API:
curl -H "Authorization: Bearer <firebase-id-token>" \ http://localhost:8080/api/user/me
La API valida el token JWT y mapea el claim sub al ExternalAuthId del usuario interno.
La integración con Sentry es opcional. La API funciona normalmente con Sentry deshabilitado.
Para habilitar el monitoreo, establece estas variables de entorno:
export Sentry__Enabled=true
export Sentry__Dsn=<tu-sentry-dsn>
export Sentry__Environment=Development
export Sentry__TracesSampleRate=0.2O configura en appsettings.Development.json:
{
"Sentry": {
"Enabled": true,
"Dsn": "<tu-sentry-dsn>",
"Environment": "Development",
"TracesSampleRate": 0.2
}
}Cuando está habilitado, los errores no manejados y las trazas de rendimiento se envían a Sentry con contexto completo (ruta, usuario, ID de correlación).
Una vez que la API esté en ejecución, navega a la interfaz Swagger para documentación interactiva:
Swagger proporciona:
- Documentación completa de endpoints con esquemas de solicitud/respuesta
- Reglas de validación y restricciones
- Pruebas interactivas (prueba endpoints directamente desde el navegador)
- Soporte de autenticación (agrega tu token Bearer para probar endpoints protegidos)
Nota: Swagger esta habilitado solo en ambientes no productivos.
Para habilitar la carga de imágenes de categorías, configura AWS S3 y CloudFront:
export AWS_ACCESS_KEY_ID=tu-access-key-id
export AWS_SECRET_ACCESS_KEY=tu-secret-access-key
export AWS_REGION=us-east-1
export AWS__S3__BucketName=lifehacking-category-images
export AWS__CloudFront__Domain=tu-distribucion.cloudfront.netPara instrucciones detalladas de configuración de AWS, consulta docs/AWS-S3-Setup-Guide.md
El proyecto sigue los principios de Arquitectura Limpia con una clara separación de responsabilidades:
lifehacking/
├── lifehacking.slnx # Archivo de solución .NET 10
├── README.md # Documentación principal (inglés)
├── AGENTS.md # Guía para agentes de IA
├── docker-compose.yml # Configuración Docker Compose
├── Dockerfile # Imagen Docker de la aplicación
│
├── ADRs/ # Architecture Decision Records
│ ├── 001-use-microsoft-testing-platform-runner.md
│ ├── 018-replace-postgresql-persistence-with-firebase-database.md
│ ├── 020-user-favorites-domain-model-and-storage.md
│ └── ... # Más decisiones arquitectónicas
│
├── docs/ # Documentación adicional
│ ├── MVP.md # Requisitos del producto y alcance MVP
│ ├── AWS-S3-Setup-Guide.md # Guía de configuración AWS S3
│ └── Search-Architecture-Decision.md
│
└── lifehacking/ # Código fuente principal
│
├── Domain/ # Capa de Dominio
│ ├── Entities/ # Entidades del dominio (User, Tip, Category, UserFavorites)
│ ├── ValueObject/ # Objetos de valor (CategoryImage, etc.)
│ ├── Primitives/ # Tipos primitivos (Result<T, TE>)
│ └── Constants/ # Constantes del dominio (ImageConstants)
│
├── Application/ # Capa de Aplicación
│ ├── UseCases/ # Casos de uso organizados por característica
│ │ ├── User/ # Casos de uso de usuarios
│ │ ├── Category/ # Casos de uso de categorías
│ │ ├── Tip/ # Casos de uso de consejos
│ │ ├── Favorite/ # Casos de uso de favoritos
│ │ └── Dashboard/ # Casos de uso del panel de control
│ ├── Dtos/ # Objetos de transferencia de datos
│ │ ├── User/ # DTOs de usuarios
│ │ ├── Category/ # DTOs de categorías
│ │ ├── Tip/ # DTOs de consejos
│ │ ├── Favorite/ # DTOs de favoritos
│ │ └── Dashboard/ # DTOs del panel de control
│ ├── Interfaces/ # Interfaces (puertos)
│ │ ├── IUserRepository
│ │ ├── ICategoryRepository
│ │ ├── IImageStorageService
│ │ └── ICacheInvalidationService
│ ├── Exceptions/ # Excepciones de aplicación
│ ├── Validation/ # Utilidades de validación
│ └── Caching/ # Definiciones de claves de caché
│
├── Infrastructure/ # Capa de Infraestructura
│ ├── Data/Firestore/ # Implementación Firestore
│ │ ├── Documents/ # Clases de documentos Firestore
│ │ └── DataStores/ # Almacenes de datos (mapeo entidad-documento)
│ ├── Repositories/ # Implementaciones de repositorios
│ ├── Storage/ # Servicios de almacenamiento en la nube
│ │ └── S3ImageStorageService.cs
│ └── Configuration/ # Clases de opciones de configuración
│
├── WebAPI/ # Capa de API Web
│ ├── Program.cs # Punto de entrada y composición raíz
│ ├── Controllers/ # Controladores REST
│ │ ├── UserController.cs
│ │ ├── AdminCategoryController.cs
│ │ ├── AdminDashboardController.cs
│ │ └── ...
│ ├── Filters/ # Filtros globales
│ │ └── GlobalExceptionFilter.cs
│ ├── Configuration/ # Configuración de servicios
│ ├── appsettings.json # Configuración base
│ ├── appsettings.Development.json
│ └── appsettings.Production.json
│
└── Tests/ # Proyectos de pruebas
├── Application.Tests/ # Pruebas de la capa de aplicación
├── Infrastructure.Tests/ # Pruebas de la capa de infraestructura
└── WebAPI.Tests/ # Pruebas de integración de la API
El proyecto sigue estrictamente las reglas de dependencia de Arquitectura Limpia:
- Domain → Sin referencias a otros proyectos (completamente independiente)
- Application → Depende solo de Domain
- Infrastructure → Depende de Application y Domain
- WebAPI → Depende de Application, Domain e Infrastructure
- Tests → Referencian solo las capas que están destinados a validar
Cliente HTTP
↓
WebAPI Controller (capa de presentación)
↓
Application Use Case (lógica de negocio)
↓
Domain Entities/Value Objects (modelo de dominio)
↓
Infrastructure Repository (acceso a datos)
↓
Firestore/Firebase (persistencia)
↓
Result<T, AppException> (respuesta)
↓
HTTP Response (mapeo a códigos de estado)
Todos los endpoints devuelven JSON y siguen RFC 7807 Problem Details para respuestas de error. Cada respuesta incluye un correlationId para trazabilidad de solicitudes.
Para esquemas completos de solicitud/respuesta, reglas de validación y pruebas interactivas, consulta la Swagger UI en http://localhost:8080/swagger cuando ejecutes la API.
-
GET /api/tip- Buscar y filtrar consejos- Parámetros de consulta:
q(término de búsqueda),categoryId,tags[],orderBy,sortDirection,pageNumber,pageSize - Devuelve resúmenes de consejos paginados con metadatos
- Parámetros de consulta:
-
GET /api/tip/{id}- Obtener detalles completos de un consejo- Devuelve consejo completo con título, descripción, pasos ordenados, categoría, etiquetas y URL de video opcional
-
GET /api/category- Listar todas las categorías disponibles- Devuelve todas las categorías no eliminadas
-
GET /api/category/{id}/tips- Obtener consejos por categoría- Parámetros de consulta:
orderBy,sortDirection,pageNumber,pageSize - Devuelve consejos paginados para la categoría especificada
- Parámetros de consulta:
-
POST /api/user- Crear perfil de usuario después de la autenticación- Se llama una vez después de la autenticación Firebase para crear el registro de usuario interno
- ID de autenticación externa derivado del token JWT
-
GET /api/user/me- Obtener perfil del usuario actual- Usuario resuelto desde el token JWT
-
PUT /api/user/me/name- Actualizar nombre de visualización del usuario actual- Actualización de perfil de autoservicio
-
DELETE /api/user/me- Eliminar cuenta del usuario actual- Eliminación suave con registro de auditoría
-
GET /api/me/favorites- Listar consejos favoritos del usuario- Parámetros de consulta:
q,categoryId,tags[],orderBy,sortDirection,pageNumber,pageSize - Devuelve favoritos paginados con detalles completos del consejo
- Parámetros de consulta:
-
POST /api/me/favorites/{tipId}- Agregar consejo a favoritos- Operación idempotente
-
DELETE /api/me/favorites/{tipId}- Eliminar consejo de favoritos -
POST /api/me/favorites/merge- Fusionar favoritos locales del almacenamiento del cliente- Acepta array de IDs de consejos del almacenamiento local
- Devuelve resumen con conteos de agregados, omitidos y fallidos
- Idempotente y soporta éxito parcial
-
POST /api/admin/tips- Crear nuevo consejo- Requerido: title, description, steps (lista ordenada), categoryId
- Opcional: tags (máx 10), videoUrl (YouTube/Instagram)
-
PUT /api/admin/tips/{id}- Actualizar consejo existente- Todos los campos actualizables
-
DELETE /api/admin/tips/{id}- Eliminación suave de consejo- Marca el consejo como eliminado, preserva los datos
-
POST /api/admin/categories/images- Subir imagen de categoría- Acepta multipart/form-data con archivo de imagen
- Valida tamaño de archivo (máx 5MB), tipo de contenido (JPEG, PNG, GIF, WebP) y bytes mágicos
- Sube a AWS S3 con nombre de archivo único basado en GUID
- Devuelve metadatos de imagen incluyendo URL de CDN CloudFront
- Requerido para crear categorías con imágenes
-
POST /api/admin/categories- Crear nueva categoría- Requerido: name (2-100 caracteres, único sin distinción de mayúsculas)
- Opcional: metadatos de imagen del endpoint de carga
-
PUT /api/admin/categories/{id}- Actualizar nombre de categoría- Aplica unicidad
-
DELETE /api/admin/categories/{id}- Eliminación suave de categoría- Cascada de eliminación suave a todos los consejos asociados
-
POST /api/admin/user- Crear usuario administrador- Crea usuario en Firebase y base de datos interna
- Requerido: email, displayName, password
-
GET /api/admin/user- Listar usuarios con paginación- Parámetros de consulta:
search,orderBy,sortDirection,pageNumber,pageSize,isDeleted - Soporta búsqueda en email, nombre e ID
- Parámetros de consulta:
-
GET /api/admin/user/{id}- Obtener usuario por ID interno -
GET /api/admin/user/email/{email}- Obtener usuario por dirección de email -
PUT /api/admin/user/{id}/name- Actualizar nombre de visualización del usuario -
DELETE /api/admin/user/{id}- Eliminación suave de cuenta de usuario
GET /api/admin/dashboard- Obtener estadísticas del panel de control- Devuelve conteos de entidades para usuarios, categorías y consejos
- Resultados en caché durante 1 hora para rendimiento óptimo
- Proporciona vista rápida para monitoreo administrativo
Esta API sigue los principios de Arquitectura Limpia (Clean Architecture) con clara separación de responsabilidades:
Responsabilidad: Contiene la lógica de negocio central y las reglas del dominio.
Características:
- Entidades del negocio (User, Tip, Category, UserFavorites)
- Objetos de valor (CategoryImage)
- Tipos primitivos del dominio (Result<T, TE>)
- Constantes del dominio (ImageConstants)
- Sin dependencias externas (completamente independiente)
- Agnóstico de persistencia y frameworks
Principio: El dominio es el corazón de la aplicación y no debe depender de nada externo.
Responsabilidad: Orquesta los casos de uso y coordina el flujo de datos.
Características:
- Casos de uso organizados por característica (User, Category, Tip, Favorite, Dashboard)
- DTOs (Data Transfer Objects) para comunicación con la capa de presentación
- Interfaces (puertos) para servicios externos (IUserRepository, ICategoryRepository, IImageStorageService)
- Lógica de validación y transformación
- Gestión de caché con invalidación automática
- Manejo de excepciones de aplicación
Principio: Define qué hace el sistema sin preocuparse por cómo lo hace.
Responsabilidad: Implementa los detalles técnicos y servicios externos.
Características:
- Implementaciones de repositorios (UserRepository, CategoryRepository)
- Acceso a datos con Firestore (documentos, data stores)
- Servicios de almacenamiento en la nube (S3ImageStorageService)
- Integración con Firebase Authentication
- Configuración de servicios externos (AWS, Firebase)
- Mapeo entre entidades de dominio y documentos de persistencia
Principio: Proporciona las implementaciones concretas de las abstracciones definidas en Application.
Responsabilidad: Expone la funcionalidad a través de endpoints HTTP REST.
Características:
- Controladores REST organizados por característica
- Middleware de autenticación y autorización
- Filtros globales (GlobalExceptionFilter)
- Configuración de servicios y composición raíz (Program.cs)
- Documentación Swagger/OpenAPI
- Mapeo de Result<T, AppException> a códigos de estado HTTP
Principio: Capa delgada enfocada en preocupaciones HTTP, delegando lógica a Application.
En lugar de lanzar excepciones para flujo de control normal, se usa el patrón Result:
Result<TipDetailResponse, AppException> result = await useCase.ExecuteAsync(request);
return result.Match(
success => Ok(success),
error => error.ToActionResult()
);Beneficios:
- Manejo explícito de errores
- Mejor rendimiento (sin stack unwinding)
- Código más predecible y testeable
Todas las dependencias se inyectan a través de constructores:
public class CreateTipUseCase
{
private readonly ITipRepository _tipRepository;
private readonly ICategoryRepository _categoryRepository;
public CreateTipUseCase(
ITipRepository tipRepository,
ICategoryRepository categoryRepository)
{
_tipRepository = tipRepository;
_categoryRepository = categoryRepository;
}
}Beneficios:
- Facilita testing con mocks
- Bajo acoplamiento
- Fácil sustitución de implementaciones
Abstrae el acceso a datos detrás de interfaces:
public interface ITipRepository
{
Task<Tip?> GetByIdAsync(Guid id);
Task<PagedResult<Tip>> SearchAsync(TipQueryCriteria criteria);
Task<Tip> CreateAsync(Tip tip);
Task UpdateAsync(Tip tip);
Task DeleteAsync(Guid id);
}Beneficios:
- Independencia de la tecnología de persistencia
- Facilita cambios de base de datos
- Mejora testabilidad
Las decisiones arquitectónicas clave están documentadas en ADRs/:
- ADR-018 - Reemplazo de PostgreSQL por Firebase Firestore
- ADR-020 - Modelo de dominio y almacenamiento de favoritos de usuario
- ADR-006 - Roles de usuario y ciclo de vida de eliminación suave
- ADR-010 - Configuración de producción endurecida
- ADR-011 - Headers de seguridad y rate limiting
- ADR-013 - Manejo estandarizado de errores y logging de seguridad
- ADR-015 - Integración de monitoreo y observabilidad con Sentry
El sistema utiliza Firebase Authentication con tokens JWT Bearer:
- Usuario se autentica con Firebase (tu frontend maneja esto)
- Frontend recibe token de ID de Firebase (JWT)
- Frontend llama a la API con el token en el header
Authorization: Bearer <token> - API valida el token con Firebase y extrae la identidad del usuario
- API mapea el UID de Firebase al registro de usuario interno
- Acceso: Sin autenticación requerida
- Permisos:
- Lectura completa de consejos y categorías
- Búsqueda y filtrado avanzado
- Gestión de favoritos del lado del cliente (local storage)
- Acceso: Requiere token JWT válido
- Permisos:
- Todos los permisos de usuarios anónimos
- Favoritos persistentes del lado del servidor
- Gestión de perfil (ver, actualizar nombre)
- Eliminación de cuenta (autoservicio)
- Fusión de favoritos locales
- Acceso: Requiere token JWT válido con rol Admin
- Permisos:
- Todos los permisos de usuarios autenticados
- Gestión completa de consejos (crear, actualizar, eliminar)
- Gestión de categorías (crear, actualizar, eliminar, subir imágenes)
- Administración de usuarios (crear, listar, actualizar, eliminar)
- Acceso al panel de control con estadísticas
Después de la autenticación con Firebase, los usuarios deben crear su perfil interno:
POST /api/user
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
{
"email": "usuario@ejemplo.com",
"name": "Juan Pérez"
}El ExternalAuthId se extrae automáticamente del token JWT (claim sub).
Los administradores pueden crearse mediante:
- Seeding en inicio - Configura
AdminUser:SeedOnStartup=truecon credenciales en variables de entorno - API de Admin - Administradores existentes pueden crear nuevos admins vía
POST /api/admin/user
Ejemplo de configuración para seeding:
{
"AdminUser": {
"SeedOnStartup": true,
"Email": "admin@ejemplo.com",
"DisplayName": "Administrador",
"Password": "ContraseñaSegura123!"
}
}La API valida automáticamente los tokens JWT usando la configuración de Firebase:
{
"Authentication": {
"Authority": "https://securetoken.google.com/<tu-project-id>",
"Audience": "<tu-project-id>"
}
}Claims importantes del JWT:
sub- UID de Firebase (mapeado a ExternalAuthId)email- Email del usuarioemail_verified- Estado de verificación del emailrole- Rol personalizado (User o Admin)
Esta API está lista para producción con medidas de seguridad exhaustivas:
- JWT Authentication - Validación de tokens basada en Firebase con autorización basada en roles
- Role-Based Access Control (RBAC) - Separación clara entre usuarios anónimos, autenticados y administradores
- Token Validation - Validación automática de firma, expiración y audiencia de tokens JWT
- Secure Claims Mapping - Mapeo seguro de claims JWT a identidad de usuario interno
Dos políticas de rate limiting para proteger contra abuso:
- Límite: 100 solicitudes por minuto
- Aplicado a: Endpoints estándar de lectura y escritura
- Ventana: 1 minuto deslizante
- Límite: 10 solicitudes por minuto
- Aplicado a: Operaciones sensibles (crear, actualizar, eliminar)
- Ventana: 1 minuto deslizante
Respuesta cuando se excede el límite:
{
"status": 429,
"type": "https://httpstatuses.io/429",
"title": "Too Many Requests",
"detail": "Rate limit exceeded. Please try again later.",
"instance": "/api/admin/tips",
"correlationId": "abc123"
}La API configura automáticamente headers de seguridad HTTP:
- Content-Security-Policy (CSP) - Previene ataques XSS
- Strict-Transport-Security (HSTS) - Fuerza conexiones HTTPS
- X-Frame-Options - Previene clickjacking
- X-Content-Type-Options - Previene MIME sniffing
- Referrer-Policy - Controla información de referrer
- Permissions-Policy - Controla características del navegador
Configuración CORS flexible para integración con frontend:
{
"ClientApp": {
"Origin": "https://tu-app.com"
}
}Características:
- Orígenes configurables por entorno
- Soporte para múltiples orígenes en producción
- Headers permitidos específicos
- Métodos HTTP permitidos controlados
Validación exhaustiva en múltiples niveles:
- Anotaciones de datos en DTOs
- Validación automática en el pipeline de ASP.NET Core
- Mensajes de error descriptivos
- Reglas de negocio en entidades
- Objetos de valor con validación incorporada
- Validación de invariantes del dominio
- Magic Byte Validation - Previene spoofing de tipo de contenido
- Sanitización de nombres de archivo - Previene vulnerabilidades de path traversal
- Validación de tamaño - Límites definidos en constantes (máx 5MB para imágenes)
- Validación de tipo MIME - Solo tipos permitidos (JPEG, PNG, GIF, WebP)
Preservación de datos con registro de auditoría:
- Usuarios - Marcados como eliminados, datos preservados
- Consejos - Marcados como eliminados, relaciones preservadas
- Categorías - Eliminación en cascada suave a consejos relacionados
- Auditoría - Timestamps de eliminación para trazabilidad
Sistema completo de logging con integración Sentry:
- Correlation IDs - Trazabilidad de solicitudes en todos los logs
- Structured Logging - Logs estructurados con contexto rico
- Security Events - Logging de eventos de seguridad (autenticación, autorización)
- Error Tracking - Captura automática de excepciones no manejadas
- Performance Monitoring - Trazas de rendimiento con sample rate configurable
Respuestas de error consistentes siguiendo RFC 7807:
{
"status": 400,
"type": "https://httpstatuses.io/400/validation-error",
"title": "Validation error",
"detail": "One or more validation errors occurred.",
"instance": "/api/admin/tips",
"correlationId": "abc123",
"errors": {
"Title": ["El título del consejo debe tener al menos 5 caracteres"]
}
}Beneficios:
- Formato estándar de la industria
- Información de error detallada sin exponer detalles de implementación
- Correlation IDs para soporte y debugging
- Respuestas consistentes en toda la API
- SQL Injection - No aplicable (NoSQL con Firestore)
- XSS (Cross-Site Scripting) - Headers CSP y sanitización de entrada
- CSRF (Cross-Site Request Forgery) - Tokens JWT stateless
- Path Traversal - Sanitización de nombres de archivo
- Content Type Spoofing - Validación de magic bytes
- Denial of Service - Rate limiting y timeouts configurables
- Information Disclosure - Mensajes de error genéricos en producción
El proyecto incluye cobertura de pruebas exhaustiva en todas las capas:
- Application.Tests - Pruebas de casos de uso y lógica de dominio
- Infrastructure.Tests - Pruebas de repositorios y acceso a datos con emulador Firestore
- WebAPI.Tests - Pruebas de integración para controladores y middleware
# Ejecutar todas las pruebas
dotnet test lifehacking.slnx
# Ejecutar pruebas de un proyecto específico
dotnet test lifehacking/Tests/Application.Tests/Application.Tests.csproj
dotnet test lifehacking/Tests/Infrastructure.Tests/Infrastructure.Tests.csproj
dotnet test lifehacking/Tests/WebAPI.Tests/WebAPI.Tests.csproj
# Ejecutar una prueba específica
dotnet test --filter "Name=CreateTip_ShouldReturnValidationError_WhenTitleIsTooShort"
# Ejecutar todas las pruebas de una clase
dotnet test --filter "FullyQualifiedName~CreateTipUseCaseTests"El proyecto utiliza Microsoft Testing Platform como runner de pruebas moderno:
<PropertyGroup>
<DotNetTestRunner>Microsoft.Testing.Platform</DotNetTestRunner>
</PropertyGroup>Beneficios:
- Rendimiento mejorado
- Mejor integración con herramientas de desarrollo
- Soporte para pruebas basadas en propiedades
Todas las pruebas usan xUnit como framework de testing:
[Fact]
public async Task CreateTip_ShouldReturnSuccess_WhenDataIsValid()
{
// Arrange
var request = new CreateTipRequest { /* ... */ };
// Act
var result = await _useCase.ExecuteAsync(request);
// Assert
result.IsSuccess.Should().BeTrue();
}Sintaxis de aserciones expresiva y legible:
// En lugar de
Assert.Equal(expected, actual);
Assert.True(condition);
// Usamos
actual.Should().Be(expected);
condition.Should().BeTrue();
result.Should().NotBeNull();
list.Should().HaveCount(5);Beneficios:
- Mensajes de error más descriptivos
- Sintaxis más natural y legible
- Mejor experiencia de desarrollo
Las pruebas de infraestructura usan el emulador local de Firestore:
# Iniciar emulador
firebase emulators:start --only firestore
# Las pruebas se conectan automáticamente al emulador
export FIRESTORE_EMULATOR_HOST=localhost:8080Beneficios:
- Pruebas de integración realistas
- Sin costos de Firebase
- Datos aislados por ejecución de prueba
- Velocidad de ejecución rápida
Todas las pruebas siguen el patrón:
{NombreDelMétodo}_Should{HacerAlgo}_When{Condición}
Ejemplos:
CreateTip_ShouldReturnSuccess_WhenDataIsValid()
CreateTip_ShouldReturnValidationError_WhenTitleIsTooShort()
GetUserById_ShouldReturnNotFound_WhenUserDoesNotExist()
AddFavorite_ShouldBeIdempotent_WhenCalledMultipleTimes()Beneficios:
- Nombres autodescriptivos
- Fácil identificación de escenarios
- Documentación viva del comportamiento
Las pruebas están organizadas por característica y capa:
Tests/
├── Application.Tests/
│ ├── UseCases/
│ │ ├── User/
│ │ │ ├── CreateUserUseCaseTests.cs
│ │ │ ├── DeleteUserUseCaseTests.cs
│ │ │ └── UpdateUserNameUseCaseTests.cs
│ │ ├── Category/
│ │ │ ├── CreateCategoryUseCaseTests.cs
│ │ │ └── DeleteCategoryUseCaseTests.cs
│ │ ├── Tip/
│ │ │ ├── CreateTipUseCaseTests.cs
│ │ │ └── SearchTipsUseCaseTests.cs
│ │ ├── Favorite/
│ │ │ ├── AddFavoriteUseCaseTests.cs
│ │ │ └── MergeFavoritesUseCaseTests.cs
│ │ └── Dashboard/
│ │ └── GetDashboardUseCaseTests.cs
│ └── MicrosoftTestingPlatformSmokeTests.cs
│
├── Infrastructure.Tests/
│ ├── Repositories/
│ │ ├── UserRepositoryTests.cs
│ │ ├── CategoryRepositoryTests.cs
│ │ └── TipRepositoryTests.cs
│ └── Storage/
│ └── S3ImageStorageServiceTests.cs
│
└── WebAPI.Tests/
├── Controllers/
│ ├── UserControllerTests.cs
│ ├── AdminCategoryControllerTests.cs
│ └── AdminDashboardControllerTests.cs
└── Filters/
└── GlobalExceptionFilterTests.cs
- Prueban casos de uso aislados
- Usan mocks para dependencias (repositorios, servicios)
- Verifican lógica de negocio y validación
- Prueban comportamiento de caché
[Fact]
public async Task GetDashboard_ShouldReturnCachedData_WhenCacheHit()
{
// Arrange
var cachedData = new DashboardResponse { /* ... */ };
_cache.Set(CacheKeys.Dashboard, cachedData);
// Act
var result = await _useCase.ExecuteAsync(new GetDashboardRequest());
// Assert
result.IsSuccess.Should().BeTrue();
_mockRepository.Verify(r => r.GetStatistics(), Times.Never);
}- Prueban repositorios con emulador Firestore
- Verifican mapeo entre entidades y documentos
- Prueban consultas y filtros complejos
- Validan comportamiento de persistencia
[Fact]
public async Task CreateUser_ShouldPersistToFirestore_WhenDataIsValid()
{
// Arrange
var user = User.Create(/* ... */);
// Act
await _repository.CreateAsync(user);
// Assert
var retrieved = await _repository.GetByIdAsync(user.Id);
retrieved.Should().NotBeNull();
retrieved.Email.Should().Be(user.Email);
}- Prueban endpoints HTTP completos
- Verifican códigos de estado y respuestas
- Validan autenticación y autorización
- Prueban manejo de errores
[Fact]
public async Task CreateTip_ShouldReturn401_WhenNotAuthenticated()
{
// Arrange
var request = new CreateTipRequest { /* ... */ };
// Act
var response = await _client.PostAsJsonAsync("/api/admin/tips", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
}El proyecto mantiene alta cobertura de pruebas:
- Casos de uso: >90% de cobertura
- Repositorios: >85% de cobertura
- Controladores: >80% de cobertura
- Lógica de dominio: 100% de cobertura
Para generar reporte de cobertura:
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencoverEl proyecto incluye documentación exhaustiva para facilitar el desarrollo:
- AGENTS.md - Guía completa para agentes de IA trabajando con este código
- ADRs/ - Architecture Decision Records documentando decisiones técnicas clave
- docs/MVP.md - Requisitos del producto y alcance del MVP
- docs/AWS-S3-Setup-Guide.md - Guía detallada de configuración de AWS S3
- Mantén dependencias estrictas entre capas
- Domain no debe depender de nada
- Application solo depende de Domain
- Infrastructure implementa interfaces de Application
- WebAPI es la capa de composición
- Usa entidades ricas con comportamiento
- Encapsula lógica de negocio en el dominio
- Usa objetos de valor para conceptos sin identidad
- Mantén agregados consistentes
// ❌ Incorrecto
if (file.Length > 5242880) { /* ... */ }
// ✅ Correcto
if (file.Length > ImageConstants.MaxFileSizeInBytes) { /* ... */ }Reglas:
- Define constantes con nombres significativos
- Centraliza valores reutilizables
- Usa enums para conjuntos de valores relacionados
- Nombres autodescriptivos que expresen intención
public class CreateTipRequest
{
[Required]
[StringLength(200, MinimumLength = 5)]
public string Title { get; set; }
[Required]
[StringLength(2000, MinimumLength = 10)]
public string Description { get; set; }
[Required]
[MinLength(1)]
public List<TipStepRequest> Steps { get; set; }
}// En lugar de lanzar excepciones
public async Task<Result<TipDetailResponse, AppException>> ExecuteAsync(
CreateTipRequest request)
{
if (!await _categoryRepository.ExistsAsync(request.CategoryId))
{
return new NotFoundException("Category not found");
}
var tip = Tip.Create(/* ... */);
await _repository.CreateAsync(tip);
return tip.ToDetailResponse();
}public class User
{
public Guid Id { get; private set; }
public string Email { get; private set; }
public DateTime CreatedAt { get; private set; }
public DateTime? DeletedAt { get; private set; }
public void Delete()
{
DeletedAt = DateTime.UtcNow;
}
public bool IsDeleted => DeletedAt.HasValue;
}Sigue Conventional Commits:
<tipo>: <descripción>
<cuerpo opcional>
<footer opcional>
Tipos permitidos:
feat- Nueva funcionalidadfix- Corrección de bugchore- Tareas de mantenimientorefactor- Refactorización de códigodocs- Cambios en documentacióntest- Agregar o modificar pruebas
Ejemplos:
feat: add user favorites merge endpoint
Implements automatic merge of local favorites when user logs in
for the first time. Handles deduplication and partial failures.
refs: WT-1234
fix: correct cache invalidation on category delete
Categories were not being removed from cache when deleted,
causing stale data to be served.
refs: WT-5678
- Ramas de características:
issue-<ticket-id>-<descripcion-corta> - Sin commits directos a la rama principal
- Pull requests requeridos para todos los cambios
- Revisión de código antes de merge
Ejemplo:
# Crear rama desde issue
git checkout -b issue-123-add-favorites-merge
# Hacer commits
git commit -m "feat: add merge favorites use case"
git commit -m "test: add merge favorites tests"
# Push y crear PR
git push origin issue-123-add-favorites-mergeCaracterísticas planificadas para futuras versiones:
- Búsqueda de texto completo con Algolia o Elasticsearch
- Sistema de comentarios y valoraciones de consejos
- Notificaciones push para nuevos consejos
- Soporte multiidioma para consejos
- Recomendaciones personalizadas basadas en IA
- Integración con redes sociales para compartir
- Estadísticas avanzadas para administradores
- Aplicación móvil nativa (iOS y Android)
- Modo offline con sincronización
- Gamificación (badges, logros, niveles)
- Comunidad de usuarios con perfiles públicos