Una aplicación completa para el control de presupuestos familiares mensuales, desarrollada con arquitectura limpia y las mejores prácticas de ingeniería de software.
Una familia necesita una aplicación para controlar sus gastos y no exceder su presupuesto mensual, con reglas de negocio estrictas que garantizan la integridad financiera.
El proyecto sigue los principios de Clean Architecture con separación clara de responsabilidades:
PresupuestoFamiliarMensual/
├── src/
│ ├── PresupuestoFamiliarMensual.Core/ # Capa de Dominio
│ │ ├── Entities/ # Entidades del dominio
│ │ ├── Interfaces/ # Contratos de repositorios
│ │ └── Exceptions/ # Excepciones de dominio
│ ├── PresupuestoFamiliarMensual.Application/ # Capa de Aplicación
│ │ ├── DTOs/ # Objetos de transferencia
│ │ ├── Services/ # Servicios de aplicación
│ │ └── Mapping/ # Configuración AutoMapper
│ ├── PresupuestoFamiliarMensual.Infrastructure/# Capa de Infraestructura
│ │ ├── Data/ # Contexto EF y UnitOfWork
│ │ └── Repositories/ # Implementaciones de repositorios
│ └── PresupuestoFamiliarMensual.API/ # Capa de Presentación
│ └── Controllers/ # Controladores REST
- FamilyMember: Miembros de la familia
- Month: Meses del año
- Budget: Presupuesto mensual
- BudgetCategory: Categorías de gasto con límites
- Expense: Gastos registrados
GET /api/budgets- Obtener todos los presupuestosGET /api/budgets/{id}- Obtener presupuesto por IDGET /api/budgets/family-member/{familyMemberId}- Presupuestos por miembroPOST /api/budgets- Crear nuevo presupuestoPUT /api/budgets/{id}- Actualizar presupuestoDELETE /api/budgets/{id}- Eliminar presupuesto
GET /api/budgets/{budgetId}/categories- Obtener categorías del presupuestoGET /api/budgets/{budgetId}/categories/{id}- Obtener categoría por IDPOST /api/budgets/{budgetId}/categories- Crear nueva categoríaPUT /api/budgets/{budgetId}/categories/{id}- Actualizar categoríaDELETE /api/budgets/{budgetId}/categories/{id}- Eliminar categoría
GET /api/budgets/{budgetId}/expenses- Obtener gastos del presupuestoGET /api/budgets/{budgetId}/expenses/{id}- Obtener gasto por IDPOST /api/budgets/{budgetId}/expenses- Registrar nuevo gastoDELETE /api/budgets/{budgetId}/expenses/{id}- Eliminar gasto
GET /api/reference-data/family-members- Obtener miembros de familiaGET /api/reference-data/months- Obtener meses disponibles
// El sistema NO permite gastos que excedan el límite de la categoría
if (createExpenseDto.Amount > remainingAmount)
{
throw new CategoryLimitExceededException(
category.Name,
currentSpent,
category.Limit,
createExpenseDto.Amount);
}// No se permiten categorías con nombre repetido en el mismo presupuesto
var existsByName = await _unitOfWork.BudgetCategories
.ExistsByNameInBudgetAsync(normalizedName, budgetId);
if (existsByName)
throw new DuplicateCategoryNameException(normalizedName, budgetId);// No se puede eliminar una categoría que tiene gastos registrados
if (category.HasExpenses)
throw new CategoryWithExpensesException(category.Name, category.Id, category.Expenses.Count);- .NET 8.0 - Framework principal
- Entity Framework Core 8.0 - ORM para base de datos
- SQL Server - Base de datos (LocalDB para desarrollo)
- AutoMapper - Mapeo entre entidades y DTOs
- Swagger/OpenAPI - Documentación de API
- Clean Architecture - Patrón arquitectónico
- Repository Pattern - Patrón de acceso a datos
- Unit of Work - Patrón para transacciones
- .NET 8.0 SDK
- SQL Server LocalDB (incluido con Visual Studio)
- Visual Studio 2022 o VS Code
-
Clonar el repositorio
git clone <repository-url> cd PresupuestoFamiliarMensual
-
Restaurar dependencias
dotnet restore
-
Crear la base de datos
cd src/PresupuestoFamiliarMensual.API dotnet ef database update -
Ejecutar la aplicación
dotnet run
-
Acceder a la documentación
- Swagger UI:
https://localhost:7001/swagger - API Base:
https://localhost:7001/api
- Swagger UI:
POST /api/budgets
Content-Type: application/json
{
"totalAmount": 5000.00,
"familyMemberId": 1,
"monthId": 1
}POST /api/budgets/1/categories
Content-Type: application/json
{
"name": "Comida",
"limit": 1000.00
}POST /api/budgets/1/expenses
Content-Type: application/json
{
"amount": 150.00,
"description": "Supermercado",
"budgetCategoryId": 1,
"familyMemberId": 1
}POST /api/budgets/1/expenses
{
"amount": 1200.00,
"description": "Gasto que excede límite",
"budgetCategoryId": 1,
"familyMemberId": 1
}Respuesta esperada:
{
"message": "No se puede registrar el gasto de $1200.00 en la categoría 'Comida'. Ya se han gastado $150.00 de $1000.00 disponibles.",
"categoryName": "Comida",
"currentSpent": 150.00,
"limit": 1000.00,
"attemptedAmount": 1200.00
}POST /api/budgets/1/categories
{
"name": "Comida",
"limit": 500.00
}Respuesta esperada:
{
"message": "Ya existe una categoría con el nombre 'Comida' en este presupuesto."
}DELETE /api/budgets/1/categories/1Respuesta esperada:
{
"message": "No se puede eliminar la categoría 'Comida' porque tiene 1 gasto(s) registrado(s). Para mantener la consistencia de los datos, primero debe eliminar todos los gastos de esta categoría."
}- ✅ Validación de monto mínimo (mayor a 0)
- ✅ Validación de límites de categoría
- ✅ Validación de nombres únicos
- ✅ Validación de integridad referencial
- ✅ Validación de datos de entrada con Data Annotations
- ✅ Manejo de excepciones personalizado
- ✅ Transacciones de base de datos
- ✅ Validación de modelo en controladores
- ✅ Respuestas HTTP apropiadas
- ✅ Logging de errores
- ✅ Consultas optimizadas con Include
- ✅ Lazy loading deshabilitado
- ✅ Índices en base de datos
- ✅ Paginación preparada para futuras implementaciones
- Fork el proyecto
- Crea una rama para tu feature (
git checkout -b feature/AmazingFeature) - Commit tus cambios (
git commit -m 'Add some AmazingFeature') - Push a la rama (
git push origin feature/AmazingFeature) - Abre un Pull Request
Este proyecto está bajo la Licencia MIT - ver el archivo LICENSE para detalles.
Desarrollado con ❤️ siguiendo las mejores prácticas de ingeniería de software.
¡Confían en tu capacidad para aplicar lo aprendido! 🚀