Ir al contenido

Arquitectura

Iskra sigue una arquitectura hexagonal (Ports & Adapters) que separa la logica de negocio de los detalles de infraestructura.

┌─────────────────────────────────────────────────┐
│ Tu Aplicacion │
│ (handlers, servicios, dominio) │
├─────────────────────────────────────────────────┤
│ Capa de Plugins │
│ (extensiones opcionales del framework) │
├─────────────────────────────────────────────────┤
│ Capa de Drivers │
│ WebPlugin │ DbDriver │ SocketDriver │ KV │ … │
├─────────────────────────────────────────────────┤
│ @iskra-bun/core │
│ App │ EventBus │ Logger │ Config │ DI │ Errors│
└─────────────────────────────────────────────────┘

Cada kit del framework expone un Driver que se registra en la App:

interface Driver {
name: string;
init(app: App): Promise<void> | void;
start?(): Promise<void> | void;
stop?(): Promise<void> | void;
}

El ciclo de vida es: register() → init() → start() → stop().

Los plugins son extensiones livianas que no tienen ciclo de vida completo:

interface Plugin {
name: string;
install(app: App): Promise<void> | void;
}

Las features son modulos del Kernel web con resolucion de dependencias:

interface Feature {
name: string;
dependencies?: string[];
peerDependencies?: string[];
initialize(kernel: Kernel): Promise<void>;
routes?(app: Hono): void;
shutdown?(): Promise<void>;
}
  • Un Driver tiene ciclo de vida completo (init/start/stop) y se registra con app.register(driver). Úsalo cuando manejes un recurso que se abre y se cierra: un servidor HTTP, una conexión de base de datos, un servidor WebSocket.
  • Un Plugin es liviano y se instala de inmediato con app.use(plugin) a través de install(app). Úsalo para extender el comportamiento sin administrar un recurso de larga duración.

Iskra usa app.context, un Map cuyas claves se tipan con AppContextRegistry (ver Core), como contenedor de DI:

// Un driver se registra en el contexto
app.context.set('db', dbDriver);
// Otro componente lo consume, tipado: DbDriver | undefined
const db = app.context.get('db');

La comunicacion entre componentes se hace via el event bus (basado en mitt). Los payloads estan tipados para los eventos declarados en AppEvents:

// Escuchar
app.on('order:created', async (ctx) => {
const order = ctx.payload;
ctx.logger.info({ order }, 'Orden creada');
});
// Emitir
app.emit('order:created', { id: 1, total: 150 });

Todos los errores del framework extienden de IskraError:

IskraError
├── ConfigError
├── DriverError
├── PluginError
├── LifecycleError
├── HttpError (web-kit)
│ ├── ValidationError
│ ├── AuthError
│ ├── ForbiddenError
│ ├── NotFoundError
│ └── ConflictError
├── ConnectionError (db-kit)
├── QueryError (db-kit)
├── MigrationError (db-kit)
├── QueueError (worker-kit)
├── JobError (worker-kit)
├── SocketConnectionError (socket-kit)
└── SocketMessageError (socket-kit)

Cada error tiene code, context (metadata) y soporte para cause (encadenamiento).