🏗
Veredicto: Monolito Modular + extracción progresiva
El sistema tiene arquitectura en capas bien intencionada pero con 3 dependencias circulares críticas y un dios objeto (SkillExecutorService, 1 873 líneas). La recomendación es refactorizar el monolito en módulos cohesivos y extraer únicamente el módulo reports como primer microservicio autónomo, dado que ya tiene su propio repositorio y no comparte base de datos con el núcleo.
Coupling Score
68/100
Acoplamiento moderado-alto
Dependencias circulares
3
auth → user → permissions → auth
Archivos problemáticos
1
SkillExecutorService.ts (1 873 líneas)
Patrón detectado
Layered
Architecture
Confianza: 85%
Cobertura de tests
12%
Módulo agents sin tests de integración
Health score global
42/100
Requiere refactorización urgente
Diagrama de Arquitectura — Componentes (Mermaid)
Vista visual
API
Express API Gateway
:3000 · 5 route groups
↓↓↓↓↓
Controllers
AuthCtrl
ClientCtrl
SkillCtrl
AgentCtrl
ReportCtrl
↓↓↓↓↓
Services
AuthSvc
ClientSvc
AgentOrch
ReportSvc
↓↓↓↓
Repos
UserRepo
ClientRepo
SkillRepo
ReportRepo
↓↓
Data
PostgreSQL
pg 8.11.3
Redis
redis 4.6.12
Stripe API
stripe 14.13
Anthropic API
skills engine
Código Mermaid generado
graph TD
GW[Express API Gateway] --> AC[AuthController]
GW --> CC[ClientController]
GW --> SC[SkillController]
GW --> AGC[AgentController]
GW --> RC[ReportController]
AC --> AS[AuthService]
CC --> CS[ClientService]
SC --> SE[SkillExecutorService]
AGC --> AO[AgentOrchestrator]
RC --> RS[ReportService]
SE --> AO
SE --> CS
SE --> RS
PS[PaymentService] --> AS
PS --> CS
PS --> SE
subgraph Repositories
UR[UserRepo]
CR[ClientRepo]
SKR[SkillRepo]
RR[ReportRepo]
end
subgraph ExternalAPIs
DB[(PostgreSQL)]
RD[(Redis)]
STR[Stripe API]
ANT[Anthropic API]
end
🔗
Análisis de Dependencias
Dependencias circulares detectadas
PaymentService
→
AuthService
→
UserRepository
→
PaymentService
SkillExecutor
→
AgentOrchestrator
→
SkillExecutor
ClientService
→
AuthService
→
ClientRepository
→
ClientService
Solución recomendada: extraer interfaces compartidas a src/shared/interfaces/ e inyectar dependencias por contrato.
Top 10 dependencias externas
| Paquete | Versión | Estado |
| express | 4.18.2 | OK |
| typescript | 5.3.3 | OK |
| stripe | 14.13.0 | OK |
| jsonwebtoken | 9.0.2 | OK |
| axios | 1.6.5 | Update |
| bcryptjs | 2.4.3 | Deprecated |
| bull | 4.12.2 | → BullMQ |
| pg | 8.11.3 | OK |
| redis | 4.6.12 | OK |
| zod | 3.22.4 | OK |
⚠
Issues Detectados (7 críticos)
Code Smells
🔴
God Object — SkillExecutorService
Clase con 30+ métodos y 1 873 líneas que mezcla ejecución, validación, reporting, caché y gestión de errores. Viola SRP gravemente.
src/services/SkillExecutorService.ts (1873 líneas)
🔴
Mixed Concerns — PaymentController
La capa de control invoca directamente lógica de negocio de auth, clients y skill execution sin pasar por un facade de pagos.
src/controllers/ClientController.ts
🟡
Layer Violation — Repos importados en Services
Varios services importan repositories directamente sin abstracción de unidad de trabajo (Unit of Work pattern).
src/services/*.ts
🟡
Missing DTOs — Models mezclados con domain
Los interfaces en src/models/ mezclan entidades de dominio y DTOs de transferencia de datos, generando acoplamiento innecesario con la API.
src/models/*.ts
Gaps de calidad
🔴
Sin tests de integración en módulo agents
El directorio tests/integration/ está vacío. AgentOrchestrator es el componente más crítico (orquesta llamadas a LLMs externos) y carece de tests de contrato.
tests/integration/ ← vacío
🔴
Error handling no estandarizado
Hay una clase AppError definida pero no todos los servicios la usan. Algunos lanzan Error genérico. Los errores de Stripe y Anthropic no se mapean a errores de dominio.
src/shared/errors.ts
🟡
Dependencia de bull (deprecated)
La librería bull 4.x está en modo mantenimiento. Migrar a BullMQ con soporte de TypeScript nativo y Redis Streams.
package.json
🔵
bcryptjs → argon2id
bcryptjs no recibe actualizaciones de seguridad activas. Migrar a argon2 (argon2id) para hashing de contraseñas con mejor resistencia a GPU attacks.
src/services/AuthService.ts
🏛
Evaluación Arquitectónica
Patrón actual: Layered Architecture
Presentación (controllers + routes)
✓ Detectado
Negocio (services)
⚠ Problemas
Acceso a datos (repositories)
✓ Detectado
Domain models / DTOs separados
✗ Mezclados
Tests en todas las capas
✗ Incompleto
Decisión: Monolito vs Microservicios
¿Equipo < 10 desarrolladores?
Sí (4 devs)
¿Fronteras de dominio bien definidas?
Parcial
¿Iteración rápida es prioridad?
Sí
¿Scaling independiente de reports?
Sí (requerido)
¿Base de datos compartida aceptable?
Parcial
Decisión: Mantener monolito modular + extraer reports como primer microservicio independiente (fase 2).
🎯
Plan de Refactorización (5 pasos)
-
1
Dividir SkillExecutorService en 5 servicios focalizados
Crear: SkillValidatorService, SkillRunnerService, SkillCacheService, SkillReporterService, SkillPermissionsService. Cada uno con <300 líneas y una única responsabilidad. Estimar: 2 sprints.
-
2
Romper dependencias circulares con interfaz compartida
Crear src/shared/interfaces/IAuthProvider.ts, IClientProvider.ts. PaymentService depende de las interfaces, no de las implementaciones. Aplicar inyección de dependencias con un IoC container (tsyringe o InversifyJS).
-
3
Añadir tests de integración al módulo agents
Implementar contract tests para AgentOrchestrator usando MSW (Mock Service Worker) para interceptar llamadas a Anthropic API. Objetivo: 80% coverage en agents antes de la siguiente release.
-
4
Separar Domain Models de DTOs
Crear src/domain/ para entidades puras y src/api/dtos/ para contratos de API. Usar Zod para validar en la capa de entrada. Esto también facilitará la extracción de ReportService como microservicio.
-
5
Extraer módulo reports como microservicio autónomo
ReportService + ReportRepository tienen la menor dependencia con el núcleo. Exponer via gRPC o REST interno. Base de datos propia (PostgreSQL schema reports). Permite escalar reports 10x sin afectar el monolito principal.
📋
Architecture Decision Records (ADRs)
ADR-001: Mantener monolito modular como base arquitectónica
Aceptado
Contexto: Equipo de 4 personas, 6 meses de historia, deuda técnica acumulada. Decisión: Refactorizar el monolito a módulos cohesivos en lugar de migrar a microservicios completos. Trade-offs: Simplicidad operacional a cambio de escalado selectivo limitado.
ADR-002: Extraer módulo reports como primer servicio independiente
Aceptado
Contexto: El equipo necesita escalar reports independientemente del core. ReportService tiene las menores dependencias externas (solo ReportRepository y su DB schema). Decisión: Extraer en Fase 2 como servicio REST interno en el mismo cluster Kubernetes. Trade-offs: Latencia de red < 2ms vs gestión de servicio adicional.
ADR-003: Migrar de bull a BullMQ para colas de skill execution
Propuesto
Contexto: bull 4.x en modo mantenimiento. La ejecución de skills de IA requiere colas confiables con retry y dead-letter queues. Decisión propuesta: Migrar a BullMQ 5.x antes del Q3 2026. Esfuerzo estimado: 1 sprint.