@empleado-juan/commons-backend
v1.1.4
Published
Shared backend code (event bus, license client, middlewares)
Downloads
42
Maintainers
Readme
@empleado-juan/commons-backend
Paquete de código compartido exclusivo para backends del sistema Empleado Juan Control.
Instalación
npm install @empleado-juan/commons-backend @empleado-juan/commonsNota: Requiere @empleado-juan/commons como peer dependency.
Características
- ✅ Event Bus Adapters: RabbitMQ y Kafka listos para usar
- ✅ License Client: Cliente de licencias con cache y WebSocket
- ✅ Factory Pattern: Cambio de broker sin modificar código
- ✅ TypeScript: Completamente tipado
- ✅ Production Ready: Manejo de errores, retry, DLQ
Contenido
🚌 Event Bus
Sistema de mensajería con soporte para múltiples brokers.
Interfaces
import type { IEventBus, IEventConsumer } from '@empleado-juan/commons-backend';Factory Pattern
import { EventBusFactory } from '@empleado-juan/commons-backend';
// Publisher
const publisher = EventBusFactory.createPublisher();
await publisher.connect();
await publisher.publish('my-topic', {
key: 'user-123',
value: { name: 'Juan' },
headers: { 'content-type': 'application/json' }
});
// Consumer
const consumer = EventBusFactory.createConsumer();
await consumer.connect();
await consumer.subscribe(['topic-1', 'topic-2']);
await consumer.start(async (topic, data) => {
console.log(`Received from ${topic}:`, data);
});Configuración
El tipo de broker se configura con variables de entorno:
# RabbitMQ (default)
MESSAGE_BROKER=rabbitmq
RABBITMQ_URL=amqp://localhost:5672
RABBITMQ_QUEUE=my-queue
# Kafka
MESSAGE_BROKER=kafka
KAFKA_BROKERS=localhost:9092
KAFKA_GROUP_ID=my-consumer-groupAdapters Directos
Si necesitas más control, puedes usar los adapters directamente:
import {
RabbitMQPublisher,
RabbitMQConsumer,
KafkaPublisher,
KafkaConsumer
} from '@empleado-juan/commons-backend';
// RabbitMQ
const rabbitmq = new RabbitMQPublisher('amqp://localhost:5672', 'my-exchange');
await rabbitmq.connect();
// Kafka
const kafka = new KafkaPublisher(['localhost:9092'], 'my-client-id');
await kafka.connect();🔐 License Client
Cliente para validación de licencias con soporte para:
- ✅ Cache (5 min TTL)
- ✅ Grace period (24-48 horas)
- ✅ WebSocket para updates en tiempo real
- ✅ Heartbeat automático
import { LicenseClient } from '@empleado-juan/commons-backend';
import type { LicenseStatus, HeartbeatMetrics } from '@empleado-juan/commons';
// Crear instancia
const licenseClient = new LicenseClient();
// Validar licencia
const status: LicenseStatus = await licenseClient.validateLicense();
if (!status.valid) {
throw new Error(`License invalid: ${status.reason}`);
}
// Conectar WebSocket para updates en tiempo real
licenseClient.connectWebSocket();
licenseClient.on('license:deactivated', (data) => {
console.error('License deactivated:', data);
process.exit(1);
});
licenseClient.on('license:updated', (data) => {
console.log('License updated:', data);
});
// Enviar heartbeat
const metrics: HeartbeatMetrics = {
employeeCount: 150,
clientCount: 5,
version: '1.0.0'
};
await licenseClient.sendHeartbeat(metrics);
// Verificar límites
const { allowed, reason } = await licenseClient.checkLimits(5);
if (!allowed) {
throw new Error(reason);
}
// Verificar feature
const hasAdvancedReports = await licenseClient.hasFeature('advanced_reports');Configuración
LICENSE_SERVER_URL=https://license.ejemplo.com
INSTALLATION_ID=your-installation-idEjemplos Completos
Event Publisher (Backend Realtime)
import { IEventBus, EventBusFactory } from '@empleado-juan/commons-backend';
import { EVENT_TOPICS, type EventMessage } from '@empleado-juan/commons';
class EventPublisher {
private eventBus: IEventBus;
constructor() {
this.eventBus = EventBusFactory.createPublisher();
}
async connect() {
await this.eventBus.connect();
}
async publishEmployeeCreated(employee: any) {
await this.eventBus.publish(EVENT_TOPICS.EMPLOYEE_CREATED, {
key: employee.id,
value: {
id: employee.id,
firstName: employee.firstName,
lastName: employee.lastName,
email: employee.email,
isActive: employee.isActive
},
headers: {
'event-type': 'employee.created',
'timestamp': new Date().toISOString()
}
});
}
}
export const eventPublisher = new EventPublisher();Event Consumer (Backend Analytics)
import { IEventConsumer, EventBusFactory } from '@empleado-juan/commons-backend';
import { EVENT_TOPICS } from '@empleado-juan/commons';
class EventConsumer {
private eventBus: IEventConsumer;
constructor() {
this.eventBus = EventBusFactory.createConsumer();
}
async connect() {
await this.eventBus.connect();
}
async subscribe() {
await this.eventBus.subscribe([
EVENT_TOPICS.EMPLOYEE_CREATED,
EVENT_TOPICS.EMPLOYEE_UPDATED,
EVENT_TOPICS.EMPLOYEE_DELETED
]);
}
async start() {
await this.eventBus.start(async (topic, data) => {
switch (topic) {
case EVENT_TOPICS.EMPLOYEE_CREATED:
await this.handleEmployeeCreated(data);
break;
case EVENT_TOPICS.EMPLOYEE_UPDATED:
await this.handleEmployeeUpdated(data);
break;
case EVENT_TOPICS.EMPLOYEE_DELETED:
await this.handleEmployeeDeleted(data);
break;
}
});
}
private async handleEmployeeCreated(data: any) {
// Guardar en DB analytics
console.log('Employee created:', data);
}
}
export const eventConsumer = new EventConsumer();License Validation Middleware
import { Request, Response, NextFunction } from 'express';
import { LicenseClient } from '@empleado-juan/commons-backend';
const licenseClient = new LicenseClient();
export async function requireValidLicense(
req: Request,
res: Response,
next: NextFunction
) {
try {
const status = await licenseClient.checkStatus();
if (!status.valid) {
return res.status(403).json({
error: 'License invalid',
reason: status.reason
});
}
if (status.warning) {
res.setHeader('X-License-Warning', status.warning);
}
next();
} catch (error) {
next(error);
}
}Arquitectura
Event Bus - Adapter Pattern
EventBusFactory
↓
├─→ RabbitMQPublisher (implements IEventBus)
├─→ KafkaPublisher (implements IEventBus)
└─→ [Future: SQSPublisher, RedisPublisher, etc.]Ventajas:
- ✅ Cambio de broker sin modificar código de negocio
- ✅ Testing fácil con mocks
- ✅ Agregar nuevos brokers sin breaking changes
License Client - Singleton con Cache
LicenseClient
├─→ HTTP: Validate license
├─→ Cache: 5 min TTL
├─→ WebSocket: Real-time updates
└─→ Grace Period: 24-48h offline toleranceCaracterísticas:
- ✅ Cache para evitar validaciones constantes
- ✅ Grace period si servidor de licencias está down
- ✅ WebSocket para notificaciones instantáneas
- ✅ EventEmitter para integración con el sistema
Features por Broker
RabbitMQ
- ✅ Confirm mode para reliability
- ✅ Dead Letter Queue (DLQ)
- ✅ Retry automático (3 intentos)
- ✅ Prefetch para backpressure
- ✅ Topic exchange para routing
Kafka
- ✅ Idempotent producer
- ✅ Consumer groups para scaling
- ✅ Partitioning por key
- ✅ Offset management automático
- ✅ Rebalancing support
Desarrollo
Setup
git clone <repo>
cd commons-backend
npm installBuild
npm run buildPublish
# Incrementar versión
npm version patch|minor|major
# Compilar
npm run build
# Publicar
npm publish --access publicDependencias
@empleado-juan/commons: ^1.0.0axios: ^1.6.2socket.io-client: ^4.7.2amqplib: ^0.10.3kafkajs: ^2.2.4
License
MIT © Empleado Juan Team
