API REST empresarial con Clean Architecture, DDD y patrones avanzados
Backend robusto y escalable siguiendo principios de Clean Architecture y Domain-Driven Design para un sistema ERP completo.
API REST empresarial construida desde cero aplicando Clean Architecture y principios de Domain-Driven Design (DDD). Este proyecto demuestra mi dominio en el diseño de sistemas backend escalables, mantenibles y orientados al dominio de negocio.
La arquitectura está diseñada para separar responsabilidades, facilitar el testing y permitir cambios en la infraestructura sin afectar la lógica de negocio.

Las capas del sistema siguen el principio de dependencia hacia adentro:
src/
├── app.ts // Entry point
├── config/ // Configuración y adaptadores
├── data/ // Conexión a base de datos
├── domain/ // Núcleo del negocio
│ ├── entities/ // Entidades de dominio ricas
│ ├── use-cases/ // Casos de uso (lógica de aplicación)
│ ├── repositories/ // Interfaces de repositorios
│ ├── dtos/ // Data Transfer Objects
│ ├── value-objects/ // Objetos de valor inmutables
│ └── errors/ // Errores personalizados del dominio
├── infrastructure/ // Implementaciones técnicas
│ ├── di/ // Dependency Injection Container
│ ├── repositories/ // Implementación de repositorios
│ ├── mappers/ // Transformadores Entity <-> Database
│ ├── services/ // Servicios de infraestructura
│ └── jobs/ // Cron Jobs programados
└── presentation/ // Capa de API REST
├── routes.ts // Router principal
├── middlewares/ // Auth, validación, errores
└── [modules]/ // Controllers por módulo
Implementé un contenedor de inyección de dependencias propio, siguiendo el patrón Service Locator + Factory, que permite:
export class DIContainer implements IDIContainer {
private services = new Map<string, any>();
private factories = new Map<string, () => any>();
registerSingleton<T>(key: string, factory: () => T): void {
this.factories.set(key, () => {
if (!this.services.has(key)) {
this.services.set(key, factory());
}
return this.services.get(key);
});
}
resolve<T>(key: string): T {
const factory = this.factories.get(key);
if (!factory) throw new Error(`Service ${key} not found`);
return factory();
}
}
Módulos registrados: AuthModule, VehicleModule, FuelModule, CustomerModule, EmployeeModule, InventoryModule, MaintenanceModule, OrderModule, EquipmentModule, TelemetryModule, CacheModule
Objetos inmutables que encapsulan validación y reglas de negocio:
export abstract class ValueObject<T extends Primitives> {
readonly value: T;
protected constructor(value: T) {
this.value = value;
}
public equals(other: ValueObject<T>): boolean {
return this.value === other.value;
}
}
Implementados: UUID, Email, Password, Cedula, RNC, PhoneNumber, EmployeeCode, Username, TrackingCode, IntegerId, FutureDate
Las entidades no son simples DTOs, contienen lógica de negocio encapsulada:
export class Order extends Entity<Order> {
public canBeAssigned(): boolean {
return (
(this.isPending() || this.isPreparing()) &&
this.isActive &&
!this.isAssigned()
);
}
public isOverdue(): boolean {
if (!this.isScheduled() || this.isDelivered()) return false;
return new Date() > this.scheduledDate!;
}
public requiresUrgentAttention(): boolean {
return (
this.isOverdue() || (this.isPending() && this.getDaysSinceOrder() > 3)
);
}
}
Cada operación de negocio está encapsulada en un Use Case independiente:
export class LoginUser implements ILoginUserUseCase {
constructor(private readonly authRepository: AuthRepository) {}
async execute(loginUserDto: LoginUserDto): Promise<TokenResponse> {
const user = await this.authRepository.login(loginUserDto);
const accessToken = await JwtAdapter.generateAccessToken({ id: user.id });
const refreshToken = await JwtAdapter.generateRefreshToken({ id: user.id });
await this.authRepository.saveRefreshToken(
user.id,
refreshToken,
JwtAdapter.getRefreshTokenExpirationDate()
);
return { user, accessToken, refreshToken };
}
}
| Módulo | Descripción | Endpoints |
|---|---|---|
| Auth | Autenticación JWT con refresh tokens, roles y permisos | /auth/* |
| Vehicles | Gestión de flota vehicular, historial de placas | /vehicles/* |
| Employees | Gestión de personal, códigos, cédulas | /employees/* |
| Fuel | Control de combustible, métricas y analytics | /fuel/* |
| Inventory | Control de materiales, stock y movimientos | /inventory/* |
| Maintenance | Mantenimientos programados, alertas, procedimientos | /maintenance/* |
| Customers | Clientes B2B, sucursales, teléfonos | /customers/* |
| Orders | Pedidos con tracking, estados e historial | /orders/* |
| Equipment | Gestión de equipos, asignaciones, reportes | /equipment/* |
| Telemetry | Datos de telemetría y GPS | /telemetry/* |
Diseño de base de datos PostgreSQL con 30+ modelos Prisma que incluyen:
User, Role, UserRole - Sistema RBAC completoRefreshToken - Tokens con expiración y revocaciónPushToken - Notificaciones push multiplataformaOrder, OrderItem, ProductOrderStatusHistory - Trazabilidad completa de estadosVehicle, VehicleTagHistoryVehicleMaintenance, MaintenanceScheduleMaintenanceAlert con prioridadesMaterial, StockMove, Unit, MaterialCategoriesFuelTank, FuelConsumption, FuelRefillEquipment, EquipmentModel, EquipmentLocationEquipmentAssignment, EquipmentReportCustomer, CustomerPhone, CustomerAddress| Categoría | Tecnología | Propósito |
|---|---|---|
| Runtime | Node.js + TypeScript | Type safety y desarrollo moderno |
| Framework | Express 5 | API REST robusta |
| ORM | Prisma | Type-safe database access |
| Base de Datos | PostgreSQL + PostGIS | Datos relacionales + geoespaciales |
| Cache | Redis / Upstash | Cache distribuida y rate limiting |
| Auth | JWT (jsonwebtoken) | Access + Refresh tokens |
| Real-time | Socket.io | WebSockets para notificaciones |
| Logging | Winston | Logs estructurados con niveles |
| Cron | node-cron | Tareas programadas (alertas, limpieza) |
| Testing | Jest + ts-jest | Unit testing con coverage |
| Containerización | Docker Compose | Desarrollo y despliegue |
Este proyecto representa mi capacidad para diseñar y construir sistemas backend empresariales siguiendo las mejores prácticas de la industria: