Skip to content

Latest commit

 

History

History
730 lines (669 loc) · 24.4 KB

File metadata and controls

730 lines (669 loc) · 24.4 KB

Locky Framework

Visión General

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.

Características Principales

1. Modelado Declarativo de Procesos

  • 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

2. Entornos de Ejecución

Cada proceso puede definir nodos que se ejecutan en diferentes entornos. Cada entorno tiene sus propios componentes de infraestructura disponibles.

Entorno: Backend (v1.0)

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: Frontend (Roadmap v2.0)

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)

Visión Multi-Entorno

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

3. Roles, Pools y Lanes (Swimlanes)

Siguiendo la notación BPMN, los procesos soportan organización por participantes:

Pools (Participantes)

Representan organizaciones o sistemas independientes que participan en el proceso.

┌────────────────────────────────────────────────────────────┐
│  Pool: Sistema de Pedidos                                       │
├────────────────────────────────────────────────────────────┤
│  Pool: Proveedor Externo                                         │
└────────────────────────────────────────────────────────────┘

Lanes (Roles/Responsables)

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]  │
└────────────┴───────────────────────────────────────────────┘

Fases (Phases)

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] │

Tipos de Lanes Soportados

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

4. Infraestructura como Código

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

5. Reutilización de Infraestructura

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

6. Modos de Ejecución Flexibles

Cada componente de infraestructura puede ejecutarse en tres modos diferentes, permitiendo adaptarse a distintos entornos:

Modo 1: In-Process (Función Local)

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

Modo 2: Standalone Process (Proceso Node.js Separado)

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

Modo 3: Native Service (Docker/Infraestructura Real)

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.

7. Interfaces de Definición Dual

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

Ejemplo: Proceso de Registro de Usuario

Flujo del Proceso

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

Arquitectura Generada

El framework interpretará este flujo y:

  1. Generará la definición del proceso con todos sus elementos BPMN
  2. Creará los microservicios necesarios:
    • Servicio de autenticación
    • Servicio de gestión de usuarios
    • Servicio de sesiones
  3. 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
  4. Establecerá las conexiones entre componentes según el flujo definido
  5. Expondrá endpoints públicos:
    • POST /api/processes/user-signup/start - Iniciar proceso
    • GET /api/processes/{instanceId}/status - Consultar estado
    • POST /api/processes/{instanceId}/cancel - Cancelar proceso

Formato JSON Ejemplo - Múltiples Procesos

{
  "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"]
    }
  }
}

Estrategias de Selección de Modo

Modo Automático (executionMode: "auto")

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)
}

Modo Explícito

Puedes forzar un modo específico:

{
  "messageQueue": {
    "provider": "rabbitmq",
    "executionMode": "native",  // Fuerza Docker siempre
    "modes": { ... }
  }
}

Modo por Componente

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
  }
}

Variables de Entorno

# 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

Ventajas de la Infraestructura Compartida

Optimización de Recursos

  • 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

Consistencia y Mantenimiento

  • 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

Referencias con @shared/

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:

  1. Crea una única instancia del componente
  2. Configura el acceso desde todos los procesos que lo necesitan
  3. Gestiona dependencias y orden de inicialización
  4. Establece políticas de acceso y seguridad

Propuesta de Valor

  • 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