Locky es un framework de orquestación de procesos de negocio que permite definir de forma declarativa flujos complejos utilizando notaciones estándar como BPMN, UML y diagramas de decisión. El framework no solo modela los procesos, sino que también genera automáticamente la infraestructura necesaria para ejecutarlos.
- Definición de procesos de negocio usando notación BPMN
- Soporte para diagramas de decisión (Decision Model and Notation)
- Generación automática de diagramas complementarios:
- ERD (Entity-Relationship Diagrams)
- Diagramas de contexto de sistema
- Diagramas de componentes y arquitectura
Cada proceso puede definir nodos que se ejecutan en diferentes entornos. Cada entorno tiene sus propios componentes de infraestructura disponibles.
Entorno principal para lógica de negocio y servicios del lado del servidor.
| Componente | Descripción |
|---|---|
service |
Microservicios para lógica de negocio |
database |
Bases de datos relacionales y NoSQL |
queue |
Colas de mensajes (RabbitMQ, Kafka, SQS) |
cache |
Sistemas de caché (Redis, Memcached) |
api |
Endpoints REST/GraphQL |
event-bus |
Sistema de eventos asíncronos |
scheduler |
Tareas programadas y cron jobs |
file-storage |
Almacenamiento de archivos (S3, local) |
Entorno para interfaces de usuario y lógica del lado del cliente.
| Componente | Descripción |
|---|---|
micro-frontend |
Aplicaciones frontend independientes |
store |
Estado global (Redux, Zustand, Pinia) |
events |
Sistema de eventos del cliente |
form |
Formularios con validación |
table |
Tablas de datos con paginación/filtros |
modal |
Diálogos y overlays |
notification |
Sistema de notificaciones UI |
router |
Navegación entre vistas |
state-machine |
Máquinas de estado para flujos UI (XState) |
Un proceso puede tener nodos en diferentes entornos, conectados de forma transparente:
[Frontend: Form] → [Backend: API] → [Backend: Service] → [Backend: Database]
↓ ↓
[Frontend: Store] ←───────────────────── [Backend: Event-Bus]
El framework genera automáticamente:
- Código cliente para el frontend
- APIs para la comunicación frontend-backend
- WebSockets/SSE para eventos en tiempo real
- Contratos tipados compartidos entre entornos
Siguiendo la notación BPMN, los procesos soportan organización por participantes:
Representan organizaciones o sistemas independientes que participan en el proceso.
┌────────────────────────────────────────────────────────────┐
│ Pool: Sistema de Pedidos │
├────────────────────────────────────────────────────────────┤
│ Pool: Proveedor Externo │
└────────────────────────────────────────────────────────────┘
Subdivisiones dentro de un Pool que representan roles, departamentos o sistemas.
┌────────────────────────────────────────────────────────────┐
│ Pool: Sistema de Pedidos │
├────────────┬───────────────────────────────────────────────┤
│ Lane: │ [○]──▶[Crear Pedido]──▶[Confirmar]──▶ │
│ Cliente │ │
├────────────┼───────────────────────────────────────────────┤
│ Lane: │ │ │
│ Ventas │ └─▶[Validar Stock]──▶[Aprobar] │
├────────────┼───────────────────────────────────────────────┤
│ Lane: │ │ │
│ Almacén │ └─▶[Preparar] │
├────────────┼───────────────────────────────────────────────┤
│ Lane: │ │ │
│ Envíos │ └─▶[Enviar] │
└────────────┴───────────────────────────────────────────────┘
Agrupación temporal o lógica de tareas en etapas del proceso.
│ Fase 1: Solicitud │ Fase 2: Procesamiento │ Fase 3: Entrega │
├───────────────────────┼───────────────────────────┼─────────────────────┤
│ [Recibir] → [Validar] │ [Procesar] → [Notificar] │ [Enviar] → [Cerrar] │
| Tipo | Descripción | Ejemplo |
|---|---|---|
role |
Rol de usuario en el sistema | Admin, Manager, User |
department |
Departamento organizacional | Ventas, RRHH, IT |
system |
Sistema o aplicación | CRM, ERP, Legacy |
environment |
Entorno de ejecución | Frontend, Backend |
actor |
Actor externo | Cliente, Proveedor |
El framework traduce automáticamente los procesos definidos en infraestructura ejecutable completa, incluyendo:
- Microservicios: Cada tarea puede mapearse a un servicio independiente
- Colas de mensajes: RabbitMQ, AWS SQS, Apache Kafka para comunicación asíncrona
- Sistemas de eventos: Event Bus para arquitecturas event-driven
- APIs REST/GraphQL: Endpoints públicos para iniciar y gestionar procesos
- Webhooks: Para integraciones externas y notificaciones
- Orquestadores: Para coordinación de procesos complejos
Cuando se definen múltiples procesos, el framework:
- Identifica componentes compartibles: Detecta automáticamente servicios, bases de datos y recursos comunes
- Optimiza recursos: Evita duplicación de infraestructura innecesaria
- Gestiona dependencias: Coordina el acceso compartido a servicios y datos
- Permite referencias explícitas: Los procesos pueden referenciar componentes existentes
Ejemplo: Si defines procesos de "SignUp", "Login" y "PasswordRecovery", todos pueden compartir:
- El mismo servicio de autenticación
- La misma base de datos de usuarios
- El mismo API Gateway
- Las mismas colas de notificaciones
Cada componente de infraestructura puede ejecutarse en tres modos diferentes, permitiendo adaptarse a distintos entornos:
El componente se ejecuta como una función dentro del mismo proceso Node.js principal.
- Uso ideal: Desarrollo local, pruebas rápidas, debugging
- Características:
- Sin dependencias externas
- Emulación en memoria
- Arranque instantáneo
- Sin overhead de red
- Ejemplos:
- Kafka → Array en memoria con patrón pub/sub
- PostgreSQL → SQLite en memoria
- RabbitMQ → Event Emitter de Node.js
- Redis → Map/Object de JavaScript
El componente se ejecuta como un proceso Node.js independiente que emula el servicio real.
- Uso ideal: Testing de integración, CI/CD, entornos sin Docker
- Características:
- Emulación funcional completa
- Comunicación inter-proceso (IPC, HTTP, TCP)
- Aislamiento de procesos
- Sin necesidad de contenedores
- Ejemplos:
- Kafka → Implementación Node.js de message broker
- PostgreSQL → Proceso con base de datos embebida (SQLite en archivo)
- RabbitMQ → Servidor Node.js con protocolo AMQP simplificado
- Redis → Servidor Node.js con comandos Redis básicos
El componente se ejecuta como el servicio nativo real en un contenedor Docker.
- Uso ideal: Staging, producción, testing de rendimiento
- Características:
- Servicio oficial completo
- Máxima fidelidad con producción
- Todas las funcionalidades nativas
- Configuración para alta disponibilidad
- Ejemplos:
- Kafka → Apache Kafka oficial en Docker
- PostgreSQL → PostgreSQL oficial en Docker
- RabbitMQ → RabbitMQ oficial en Docker
- Redis → Redis oficial en Docker
El framework selecciona automáticamente el modo según el entorno, o permite especificarlo explícitamente en la configuración.
Los desarrolladores pueden crear flujos mediante:
- JSON estructurado: Definición programática y versionable de procesos
- Editor gráfico web: Interfaz visual drag-and-drop para modelado intuitivo
Evento de Inicio:
- El sistema recibe una solicitud de registro (SignUp)
Tarea 1: Validación de Usuario
- Búsqueda en base de datos para verificar existencia del usuario
Gateway Condicional:
Rama 1: Usuario ya registrado
- Tarea 2a: Enviar respuesta HTTP con error "Email ya en uso"
- Evento de Fin
Rama 2: Usuario nuevo
- Tarea 2b: Crear registro de usuario en base de datos
- Tarea 3: Generar token de sesión
- Tarea 4: Iniciar sesión automática
- Evento de Fin
El framework interpretará este flujo y:
- Generará la definición del proceso con todos sus elementos BPMN
- Creará los microservicios necesarios:
- Servicio de autenticación
- Servicio de gestión de usuarios
- Servicio de sesiones
- Configurará la infraestructura:
- Base de datos para usuarios
- API REST para iniciar el proceso de registro
- Cola de mensajes para procesamiento asíncrono
- Event Bus para notificaciones de eventos de negocio
- Sistema de mensajería entre servicios
- Contenedores Docker para cada componente
- API Gateway con rate limiting y autenticación
- Establecerá las conexiones entre componentes según el flujo definido
- Expondrá endpoints públicos:
POST /api/processes/user-signup/start- Iniciar procesoGET /api/processes/{instanceId}/status- Consultar estadoPOST /api/processes/{instanceId}/cancel- Cancelar proceso
{
"pools": [
{
"id": "main-system",
"name": "Sistema Principal",
"lanes": [
{
"id": "frontend",
"name": "Interfaz de Usuario",
"type": "environment",
"environment": "frontend"
},
{
"id": "backend-api",
"name": "API Layer",
"type": "system",
"environment": "backend"
},
{
"id": "backend-services",
"name": "Servicios de Negocio",
"type": "system",
"environment": "backend"
}
]
},
{
"id": "external-services",
"name": "Servicios Externos",
"lanes": [
{
"id": "email-provider",
"name": "Proveedor de Email",
"type": "actor"
}
]
}
],
"phases": [
{
"id": "phase-request",
"name": "Solicitud",
"order": 1
},
{
"id": "phase-validation",
"name": "Validación",
"order": 2
},
{
"id": "phase-completion",
"name": "Finalización",
"order": 3
}
],
"roles": [
{
"id": "anonymous",
"name": "Usuario Anónimo",
"permissions": ["signup", "login", "password-recovery"]
},
{
"id": "authenticated",
"name": "Usuario Autenticado",
"permissions": ["*"]
},
{
"id": "admin",
"name": "Administrador",
"permissions": ["*"],
"inherits": ["authenticated"]
}
],
"processes": [
{
"id": "user-signup",
"name": "Proceso de Registro de Usuario",
"pool": "main-system",
"startEvent": {
"id": "receive-signup-request",
"lane": "frontend",
"phase": "phase-request",
"allowedRoles": ["anonymous"]
},
"tasks": [
{
"id": "validate-user",
"type": "ServiceTask",
"lane": "backend-api",
"phase": "phase-validation",
"environment": "backend",
"service": "@shared/user-service",
"action": "checkUserExists"
},
{
"id": "conditional-gateway",
"type": "ExclusiveGateway",
"lane": "backend-services",
"phase": "phase-validation",
"conditions": [
{
"when": "userExists == true",
"then": "send-error-response"
},
{
"when": "userExists == false",
"then": "create-user"
}
]
},
{
"id": "create-user",
"type": "ServiceTask",
"lane": "backend-services",
"phase": "phase-completion",
"environment": "backend",
"service": "@shared/user-service",
"action": "createUser",
"emits": ["UserCreated"]
},
{
"id": "send-welcome-email",
"type": "ServiceTask",
"lane": "email-provider",
"pool": "external-services",
"phase": "phase-completion",
"async": true,
"queue": "@shared/email-queue",
"service": "@shared/notification-service",
"action": "sendWelcomeEmail"
}
]
},
{
"id": "user-login",
"name": "Proceso de Inicio de Sesión",
"pool": "main-system",
"startEvent": {
"id": "receive-login-request",
"lane": "frontend",
"phase": "phase-request",
"allowedRoles": ["anonymous"]
},
"tasks": [
{
"id": "validate-credentials",
"type": "ServiceTask",
"lane": "backend-api",
"phase": "phase-validation",
"environment": "backend",
"service": "@shared/auth-service",
"action": "validateCredentials"
},
{
"id": "create-session",
"type": "ServiceTask",
"lane": "backend-services",
"phase": "phase-completion",
"environment": "backend",
"service": "@shared/session-service",
"action": "createSession"
}
]
},
{
"id": "password-recovery",
"name": "Proceso de Recuperación de Contraseña",
"pool": "main-system",
"startEvent": {
"id": "receive-recovery-request",
"lane": "frontend",
"phase": "phase-request",
"allowedRoles": ["anonymous"]
},
"tasks": [
{
"id": "verify-user",
"type": "ServiceTask",
"lane": "backend-api",
"phase": "phase-validation",
"environment": "backend",
"service": "@shared/user-service",
"action": "findUserByEmail"
},
{
"id": "generate-token",
"type": "ServiceTask",
"lane": "backend-services",
"phase": "phase-validation",
"environment": "backend",
"service": "@shared/auth-service",
"action": "generateRecoveryToken"
},
{
"id": "send-recovery-email",
"type": "ServiceTask",
"lane": "email-provider",
"pool": "external-services",
"phase": "phase-completion",
"async": true,
"service": "@shared/notification-service",
"action": "sendRecoveryEmail",
"queue": "@shared/email-queue"
}
]
}
],
"infrastructure": {
"api": {
"type": "rest",
"basePath": "/api/processes",
"shared": true,
"endpoints": [
{
"path": "/user-signup/start",
"method": "POST",
"triggerProcess": "user-signup",
"authentication": "none"
},
{
"path": "/user-login/start",
"method": "POST",
"triggerProcess": "user-login",
"authentication": "none"
},
{
"path": "/password-recovery/start",
"method": "POST",
"triggerProcess": "password-recovery",
"authentication": "none"
}
]
},
"messageQueue": {
"provider": "rabbitmq",
"executionMode": "auto",
"modes": {
"in-process": {
"enabled": true,
"implementation": "EventEmitter"
},
"standalone": {
"enabled": true,
"port": 5672,
"implementation": "node-amqp-emulator"
},
"native": {
"enabled": true,
"dockerImage": "rabbitmq:3.12-management",
"port": 5672,
"managementPort": 15672
}
},
"queues": [
{
"name": "user-creation-queue",
"durable": true,
"linkedTo": "create-user"
},
{
"name": "email-notification-queue",
"alias": "@shared/email-queue",
"durable": true,
"sharedBy": ["user-signup", "password-recovery"]
}
]
},
"eventBus": {
"provider": "kafka",
"executionMode": "auto",
"modes": {
"in-process": {
"enabled": true,
"implementation": "MemoryEventBus"
},
"standalone": {
"enabled": true,
"port": 9092,
"implementation": "kafkajs-server"
},
"native": {
"enabled": true,
"dockerImage": "confluentinc/cp-kafka:latest",
"port": 9092
}
},
"topics": [
{
"name": "user-events",
"events": ["UserCreated", "UserLoginFailed"]
}
]
},
"services": [
{
"name": "auth-service",
"alias": "@shared/auth-service",
"type": "microservice",
"runtime": "node",
"database": "@shared/users-db",
"exposedPorts": [3001],
"sharedBy": ["user-signup", "user-login"],
"actions": ["validateCredentials", "hashPassword"]
},
{
"name": "user-service",
"alias": "@shared/user-service",
"type": "microservice",
"runtime": "node",
"database": "@shared/users-db",
"exposedPorts": [3002],
"sharedBy": ["user-signup", "user-login", "password-recovery"],
"consumes": ["user-creation-queue"],
"publishes": ["user-events"]
},
{
"name": "session-service",
"alias": "@shared/session-service",
"type": "microservice",
"runtime": "node",
"database": "@shared/sessions-db",
"exposedPorts": [3003],
"sharedBy": ["user-signup", "user-login"]
},
{
"name": "notification-service",
"alias": "@shared/notification-service",
"type": "microservice",
"runtime": "node",
"exposedPorts": [3004],
"sharedBy": ["user-signup", "password-recovery"],
"consumes": ["@shared/email-queue"]
}
],
"databases": [
{
"name": "users-db",
"alias": "@shared/users-db",
"type": "postgresql",
"sharedBy": ["user-signup", "user-login", "password-recovery"],
"executionMode": "auto",
"modes": {
"in-process": {
"enabled": true,
"implementation": "sqlite-memory"
},
"standalone": {
"enabled": true,
"implementation": "sqlite-file",
"path": "./data/users.db"
},
"native": {
"enabled": true,
"dockerImage": "postgres:16-alpine",
"port": 5432
}
}
},
{
"name": "sessions-db",
"alias": "@shared/sessions-db",
"type": "redis",
"sharedBy": ["user-signup", "user-login"],
"executionMode": "auto",
"modes": {
"in-process": {
"enabled": true,
"implementation": "memory-map"
},
"standalone": {
"enabled": true,
"implementation": "redis-emulator",
"port": 6379
},
"native": {
"enabled": true,
"dockerImage": "redis:7-alpine",
"port": 6379
}
}
}
],
"gateway": {
"type": "api-gateway",
"provider": "kong",
"features": ["rate-limiting", "cors", "logging"]
}
}
}El framework selecciona el modo más apropiado según el entorno:
// Detección automática
if (process.env.NODE_ENV === 'production') {
→ Modo: native (Docker)
} else if (process.env.CI === 'true') {
→ Modo: standalone (Proceso Node.js)
} else {
→ Modo: in-process (Función local)
}Puedes forzar un modo específico:
{
"messageQueue": {
"provider": "rabbitmq",
"executionMode": "native", // Fuerza Docker siempre
"modes": { ... }
}
}Diferentes componentes pueden usar diferentes modos simultáneamente:
{
"databases": [
{
"name": "users-db",
"executionMode": "native" // PostgreSQL real en Docker
}
],
"messageQueue": {
"executionMode": "in-process" // Cola en memoria
}
}# Forzar modo para todos los componentes
LOCKY_EXECUTION_MODE=standalone npm start
# Modo por tipo de componente
LOCKY_DATABASE_MODE=native \
LOCKY_QUEUE_MODE=in-process \
LOCKY_CACHE_MODE=standalone \
npm start- Un solo servicio de autenticación para todos los procesos relacionados con usuarios
- Una base de datos unificada evita sincronización y duplicación de datos
- Colas compartidas reducen la complejidad operacional
- API Gateway único simplifica la gestión de seguridad y monitoreo
- Lógica de negocio centralizada: Cambios en un servicio afectan todos los procesos que lo usan
- Configuración unificada: Credenciales, secrets y configuraciones en un solo lugar
- Versionado coherente: Actualizaciones coordinadas de componentes compartidos
El framework usa el prefijo @shared/ para indicar componentes reutilizables:
@shared/auth-service → Servicio compartido
@shared/users-db → Base de datos compartida
@shared/email-queue → Cola compartida
Cuando el framework detecta estas referencias, automáticamente:
- Crea una única instancia del componente
- Configura el acceso desde todos los procesos que lo necesitan
- Gestiona dependencias y orden de inicialización
- Establece políticas de acceso y seguridad
- Reducción de tiempo de desarrollo: De semanas a horas
- Consistencia arquitectónica: Patrones probados automáticamente aplicados
- Documentación automática: Los diagramas siempre reflejan el estado actual
- Escalabilidad: Infraestructura de microservicios lista para producción
- Trazabilidad: Del proceso de negocio al código ejecutable
- Eficiencia de recursos: Componentes compartidos minimizan costos operacionales
- Mantenibilidad: Cambios centralizados en lugar de duplicados
- Flexibilidad de desarrollo: Mismo código funciona desde desarrollo local hasta producción
- Testing simplificado: Pruebas rápidas sin dependencias externas
- Transición suave: Migración gradual desde emulación hasta infraestructura real