Arquitectura
Wippy es un sistema de capas construido en Go. Los componentes se inicializan en orden de dependencias, se comunican a través de un event bus, y ejecutan procesos Lua vía un scheduler de work-stealing.
Esta es una referencia de implementación. Los diagramas y tipos Go describen componentes internos del entorno de ejecución, no entradas del registro de aplicaciones ni API de extensión.
Capas
| Capa | Componentes |
|---|---|
| Application | Procesos Lua, funciones, workflows |
| Runtime | Motor Lua (gopher-lua), 40+ módulos |
| Services | HTTP, Queue, Storage, Temporal |
| System | Topology, Factory, Functions, Contracts |
| Core | Scheduler, Registry, Dispatcher, EventBus, Relay |
| Infrastructure | AppContext, Logger, Transcoder |
Cada capa depende solo de capas debajo de ella. La capa Core proporciona primitivos fundamentales, mientras Services construye abstracciones de nivel más alto encima.
Secuencia de arranque :id=secuencia-de-boot
El arranque de la aplicación transcurre en cuatro fases.
Fase 1: Infraestructura
Crea infraestructura core antes de que cualquier componente cargue:
| Componente | Propósito |
|---|---|
| AppContext | Diccionario sellado para referencias de componentes |
| EventBus | Pub/sub para comunicación entre componentes |
| Transcoder | Serialización de payload (JSON, YAML, Lua) |
| Logger | Logging estructurado con streaming de eventos |
| Relay | Routing de mensajes (Node, Router, Mailbox) |
Fase 2: Carga de Componentes
El Loader resuelve dependencias vía ordenamiento topológico y carga componentes nivel por nivel, un componente a la vez.
Las aristas de dependencia determinan los niveles; grupos de paquetes como Core y System no imponen un orden global independiente. Por ello, los componentes sin una arista de dependencia pueden cargarse en el mismo nivel con independencia del grupo del paquete.
Cada componente se adjunta al contexto durante Load, haciendo servicios disponibles a componentes dependientes.
Fase 3: Activación
Después de que todos los componentes cargan:
- Iniciar los servicios de runtime - Llama a
StartRuntimeServices(ctx) - Congelar el Dispatcher - Bloquea el registro de handlers de comandos para permitir búsquedas sin bloqueo
- Sellar AppContext - Impide nuevas escrituras y habilita lecturas sin bloqueo
- Iniciar componentes - Llama a
Start()en cada componente con interfazStarter
Fase 4: Carga de Entradas
Las entradas del registro procedentes de los manifiestos de proyecto _index.json, _index.yaml y _index.yml se cargan y validan:
- Entradas parseadas de archivos de proyecto
- Etapas de pipeline transforman entradas (override, link, bytecode)
- Servicios marcados
auto_start: truecomienzan a ejecutar - Supervisor monitorea servicios registrados
Componentes
Los componentes son servicios Go que participan en el ciclo de vida de la aplicación.
Fases de Ciclo de Vida
| Fase | Método | Propósito |
|---|---|---|
| Load | Load(ctx) (ctx, error) |
Inicializar y adjuntar al contexto |
| Start | Start(ctx) error |
Comenzar operación activa |
| Stop | Stop(ctx) error |
Apagado ordenado |
Los componentes declaran dependencias. El loader construye un grafo acíclico dirigido y ejecuta en orden topológico. El apagado ocurre en orden inverso.
Componentes Estándar
| Componente | Dependencias | Propósito |
|---|---|---|
| PIDGen | ninguna | Generación de ID de proceso |
| Dispatcher | PIDGen | Despacho de handlers de comandos |
| Registry | Artifact | Almacenamiento y versionado de entradas |
| Finder | Registry | Lookup y búsqueda de entradas |
| Supervisor | Registry | Políticas de reinicio de servicios |
| Topology | ninguna | Árbol padre/hijo de procesos |
| Lifecycle | Topology | Gestión de ciclo de vida de servicios |
| Factory | ninguna | Generación de procesos |
| Functions | Registry | Ejecución de funciones mediante pool |
Bus de eventos :id=event-bus
Pub/sub asíncrono para comunicación entre componentes.
Diseño
- Una sola goroutine dispatcher procesa todos los eventos
- Los publishers encolan acciones sin esperar a que se entreguen a los suscriptores
- Pattern matching soporta valores exactos,
*,**y alternancia de segmentos - Ciclo de vida basado en contexto vincula suscripciones a cancelación
Flujo de Eventos
sequenceDiagram
participant P as Publisher
participant B as EventBus
participant S as Subscribers
P->>B: Send(ctx, Event)
B->>B: Match patterns
B->>S: Deliver on subscriber channel
S->>S: Execute callback
Tópicos Comunes
Cada evento lleva un System y un Kind. Los sistemas integrados publican:
| Sistema | Tipo | Propósito |
|---|---|---|
registry |
entry.create, entry.update, entry.delete, entry.accept, entry.reject |
Mutaciones de entradas |
registry |
registry.begin, registry.commit, registry.discard |
Límites de transacción |
process |
factory.register, factory.delete, factory.accept, factory.reject |
Registro de factory para tipos de proceso |
supervisor |
service.register, service.remove, service.update, service.start, service.stop |
Ciclo de vida de servicio |
Registry
Almacenamiento versionado para definiciones de entradas.
Características
- Versioned State - Cada mutación crea nueva versión
- History - Historial respaldado por SQLite para audit trail
- Event-driven - Publica eventos en mutaciones
Ciclo de Vida de Entrada
flowchart LR
YAML[YAML Files] --> Parser
Parser --> Stages[Pipeline Stages]
Stages --> Registry
Registry --> Validation
Validation --> Active
Etapas de pipeline transforman entradas:
| Etapa | Propósito |
|---|---|
| Override | Aplicar overrides de config |
| Deshabilitar | Eliminar entradas por patrón |
| Link | Resolver requirements y dependencias |
| Bytecode | Compilar Lua a bytecode |
| EmbedFS | Recolectar entradas de filesystem |
Relay
Routing de mensajes entre procesos a través de nodos.
Routing de Tres Niveles
flowchart LR
subgraph Router
Local[Local Node] --> Peer[Registered Peers]
Peer --> Inter[Internode]
end
Local -.- L[Este nodo]
Peer -.- P[Receptor peer registrado]
Inter -.- I[Otros nodos del cluster]
- Local - Entrega directa dentro del mismo nodo
- Peer - Entrega a un receptor registrado para ese ID de nodo (un peer externo, como un worker de Temporal)
- Internode - Recurre al transporte internode del cluster, instalado por el componente de cluster tras el arranque
Mailbox
Cada nodo tiene un mailbox con pool de workers:
- Hashing FNV-1a asigna remitentes a workers
- Preserva ordenamiento de mensajes por remitente
- Workers procesan mensajes concurrentemente
- Back-pressure cuando cola se llena
AppContext
Diccionario sellado para referencias de componentes.
| Propiedad | Comportamiento |
|---|---|
| Antes del sellado | Escrituras de un solo hilo durante el arranque |
| Después del sellado | Lecturas sin bloqueo, pánico en escritura |
| Duplicate keys | Panic |
| Seguridad de tipos | Funciones de obtención tipadas |
Los componentes adjuntan servicios durante la fase Load. Cuando termina el arranque, AppContext se sella, lo que permite lecturas sin bloqueos e impide nuevas escrituras.
Apagado :id=shutdown
El apagado ordenado procede en orden inverso de dependencias:
- SIGINT/SIGTERM inicia el apagado
- Supervisor detiene servicios gestionados
- Componentes con interfaz
StopperrecibenStop() - Limpieza de infraestructura
Segunda señal fuerza salida inmediata.
Ver También
- Scheduler - Ejecución de procesos
- Bus de eventos - Sistema pub/sub
- Registry - Gestión de estado
- Command Dispatch - Manejo de yields